mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d93645d690 | |||
| c3bb6307b8 | |||
| 6208c0626a | |||
| 3949c62f6f | |||
| 8c6c2ef30e | |||
| 12c84469fd | |||
| 0a8e726d05 | |||
| 533df585fd | |||
| c4eb2d5fba | |||
| 35029ad010 | |||
| 296079f70a | |||
| 4f1c73d592 | |||
| 1fb859729e | |||
| bbeb0f8445 | |||
| 2ae44cb794 | |||
| a0b8033088 | |||
| 9cc74839b0 | |||
| b7b7002855 | |||
| 55e428a4d7 | |||
| cb09223a56 | |||
| 28db3523d0 | |||
| f15e32cdb1 | |||
| 094057e265 | |||
| 025a2198a4 | |||
| 2b938b78aa | |||
| de36fc5259 | |||
| e3e5b6422a | |||
| 38343be0fa | |||
| 957e612720 | |||
| 1d2316caef | |||
| 64539853ca | |||
| 686c331b69 | |||
| 01551f98d5 | |||
| 0e2c5907e0 | |||
| 68ff79e4c3 | |||
| 5b96d86538 | |||
| 4043137cd7 | |||
| 735d8f5493 | |||
| 28efa7d120 | |||
| ebde75eeda | |||
| a645ec0fc0 | |||
| 761a185938 | |||
| 890b15d158 | |||
| 07677668a3 | |||
| fde914fd1c | |||
| 35c97a07aa | |||
| 45e3f016f4 | |||
| 38b520ab4c | |||
| 3b6920271e | |||
| a3146409da | |||
| 76aa8e4a52 | |||
| f5e6f4a8cb | |||
| 80cb1553a3 | |||
| 706f59b9d9 | |||
| 02cc902b90 | |||
| ff2fb42868 | |||
| 451bf7fbc6 | |||
| d2a4a8b0f3 | |||
| fe5753d34d |
@@ -9,6 +9,10 @@
|
|||||||
tags
|
tags
|
||||||
TAGS
|
TAGS
|
||||||
|
|
||||||
|
# Ignore coverage data
|
||||||
|
*.gcda
|
||||||
|
*.gcno
|
||||||
|
|
||||||
#
|
#
|
||||||
# Ignore generated executables and other generated files
|
# Ignore generated executables and other generated files
|
||||||
#
|
#
|
||||||
@@ -25,6 +29,7 @@ dasdview/dasdview
|
|||||||
dump2tar/src/dump2tar
|
dump2tar/src/dump2tar
|
||||||
fdasd/fdasd
|
fdasd/fdasd
|
||||||
hmcdrvfs/hmcdrvfs
|
hmcdrvfs/hmcdrvfs
|
||||||
|
hsavmcore/check-dep-fuse
|
||||||
hsavmcore/hsavmcore
|
hsavmcore/hsavmcore
|
||||||
hyptop/hyptop
|
hyptop/hyptop
|
||||||
ip_watcher/xcec-bridge
|
ip_watcher/xcec-bridge
|
||||||
@@ -98,7 +103,9 @@ zipl/boot/*.bin
|
|||||||
zipl/boot/*.exec
|
zipl/boot/*.exec
|
||||||
zipl/boot/data.h
|
zipl/boot/data.h
|
||||||
zipl/src/chreipl_helper.device-mapper
|
zipl/src/chreipl_helper.device-mapper
|
||||||
|
zdev/src/zdev_id
|
||||||
zipl/src/zipl
|
zipl/src/zipl
|
||||||
|
zipl/src/zipl-editenv
|
||||||
zipl/src/zipl_helper.device-mapper
|
zipl/src/zipl_helper.device-mapper
|
||||||
zkey/check-dep-zkey
|
zkey/check-dep-zkey
|
||||||
zkey/check-dep-zkey-cryptsetup
|
zkey/check-dep-zkey-cryptsetup
|
||||||
|
|||||||
@@ -1,5 +1,34 @@
|
|||||||
Release history for s390-tools (MIT version)
|
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)__
|
* __v2.17.0 (2021-07-07)__
|
||||||
|
|
||||||
For Linux kernel version: 5.12 / 5.13
|
For Linux kernel version: 5.12 / 5.13
|
||||||
|
|||||||
@@ -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 definitions
|
||||||
include common.mak
|
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 \
|
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||||
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||||
vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
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 \
|
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
|
||||||
genprotimg lsstp hsci hsavmcore
|
genprotimg lsstp hsci hsavmcore
|
||||||
|
|
||||||
SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS)
|
SUB_DIRS = $(BASELIB_DIRS) $(LIB_DIRS) $(TOOL_DIRS)
|
||||||
|
|
||||||
all: $(TOOL_DIRS)
|
all: $(TOOL_DIRS)
|
||||||
clean: $(TOOL_DIRS)
|
clean: $(TOOL_DIRS)
|
||||||
@@ -26,9 +32,10 @@ MAKECMDGOALS = all
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
#
|
#
|
||||||
# We have to build the libraries before the tools are built. Otherwise
|
# We have to build the base libraries before the other libraries are built,
|
||||||
# the tools would trigger parallel "make -C" builds for libraries in
|
# and then build the other libraries before the tools are built. Otherwise the
|
||||||
# case of "make -j".
|
# 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
|
# MAKECMDGOALS contains the list of goals, e.g. "clean all". We use
|
||||||
# "foreach" to generate a ";" separated list of "make -C <target>".
|
# "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) ;)
|
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||||
.PHONY: $(TOOL_DIRS)
|
.PHONY: $(TOOL_DIRS)
|
||||||
|
|
||||||
$(LIB_DIRS):
|
$(LIB_DIRS): $(BASELIB_DIRS)
|
||||||
$(foreach goal,$(MAKECMDGOALS), \
|
$(foreach goal,$(MAKECMDGOALS), \
|
||||||
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||||
.PHONY: $(LIB_DIRS)
|
.PHONY: $(LIB_DIRS)
|
||||||
|
|
||||||
|
$(BASELIB_DIRS):
|
||||||
|
$(foreach goal,$(MAKECMDGOALS), \
|
||||||
|
$(MAKE) -C $@ TOPDIR=$(TOPDIR) ARCH=$(ARCH) $(goal) ;)
|
||||||
|
.PHONY: $(BASELIB_DIRS)
|
||||||
|
|||||||
@@ -109,6 +109,9 @@ Package contents
|
|||||||
feature. Those traces are filtered with the zfcpdbf script, i.e. merge
|
feature. Those traces are filtered with the zfcpdbf script, i.e. merge
|
||||||
several traces, make it more readable etc.
|
several traces, make it more readable etc.
|
||||||
|
|
||||||
|
* sclpdbf:
|
||||||
|
Display debug data for the sclp kernel component.
|
||||||
|
|
||||||
* scsi_logging_level:
|
* scsi_logging_level:
|
||||||
Create, get or set the logging level for the SCSI logging facility.
|
Create, get or set the logging level for the SCSI logging facility.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ COMMON_INCLUDED = true
|
|||||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||||
VERSION = 2
|
VERSION = 2
|
||||||
RELEASE = 17
|
RELEASE = 18
|
||||||
PATCHLEVEL = 0
|
PATCHLEVEL = 0
|
||||||
DISTRELEASE = build-$(shell date +%Y%m%d)
|
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||||
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||||
|
|||||||
+12
-11
@@ -17,6 +17,7 @@
|
|||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@@ -53,7 +54,7 @@ static unsigned char *ioctlbuffer;
|
|||||||
static bool allcpu;
|
static bool allcpu;
|
||||||
|
|
||||||
static unsigned int max_possible_cpus; /* No of possible CPUs */
|
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 */
|
char *name; /* Counter name */
|
||||||
bool hitcnt; /* Counter number read from ioctl() */
|
bool hitcnt; /* Counter number read from ioctl() */
|
||||||
unsigned long total; /* Total counter value */
|
unsigned long total; /* Total counter value */
|
||||||
@@ -240,7 +241,7 @@ static char *show_ctrset(unsigned long set)
|
|||||||
/* Parse CPU list and counter sets */
|
/* Parse CPU list and counter sets */
|
||||||
static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
|
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;
|
unsigned long i, no_a, no_b;
|
||||||
char *cp, *tokens[16]; /* Used to parse command line params */
|
char *cp, *tokens[16]; /* Used to parse command line params */
|
||||||
char cpubuf[256];
|
char cpubuf[256];
|
||||||
@@ -275,8 +276,8 @@ static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
|
|||||||
show_ctrset(noton));
|
show_ctrset(noton));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ",")) != 0;
|
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ","));
|
||||||
++i, parm = 0) {
|
++i, parm = NULL) {
|
||||||
cp = strchr(tokens[i], '-'); /* Range character? */
|
cp = strchr(tokens[i], '-'); /* Range character? */
|
||||||
if (cp) {
|
if (cp) {
|
||||||
no_a = getnumber(tokens[i], *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 */
|
/* Convert the CPU list to a bitmask for kernel cpumask_t */
|
||||||
for (i = 0, no_b = 0; i < max_possible_cpus; ++i) {
|
for (i = 0, no_b = 0; i < max_possible_cpus; ++i) {
|
||||||
if (check[i].cpu_req) {
|
if (check[i].cpu_req) {
|
||||||
no_a = i % __BITS_PER_LONG;
|
no_a = i % LONG_BIT;
|
||||||
no_b = i / __BITS_PER_LONG;
|
no_b = i / LONG_BIT;
|
||||||
words[no_b] |= 1ULL << no_a;
|
words[no_b] |= 1ULL << no_a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* no_b is highest used index, swap array */
|
/* no_b is highest used index, swap array */
|
||||||
start->cpumask_len = (no_b + 1) * 8;
|
start->cpumask_len = (no_b + 1) * 8;
|
||||||
for (no_a = 0; no_a < no_b; ++no_a, --no_b) {
|
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_a] = words[no_b];
|
||||||
words[no_b] = tmp;
|
words[no_b] = tmp;
|
||||||
@@ -326,7 +327,7 @@ static bool check_setpossible(void)
|
|||||||
}
|
}
|
||||||
parm = cpubuf;
|
parm = cpubuf;
|
||||||
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ","));
|
for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ","));
|
||||||
++i, parm = 0) {
|
++i, parm = NULL) {
|
||||||
cp = strchr(tokens[i], '-');
|
cp = strchr(tokens[i], '-');
|
||||||
if (cp) { /* Range */
|
if (cp) { /* Range */
|
||||||
no_a = getnumber(tokens[i], *cp);
|
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 */
|
/* Iterate over all counters in each set */
|
||||||
for (unsigned int k = 0; k < sp->no_cnts; ++k) {
|
for (unsigned int k = 0; k < sp->no_cnts; ++k) {
|
||||||
__u64 value;
|
uint64_t value;
|
||||||
void *addr = base + offset;
|
void *addr = base + offset;
|
||||||
size_t idx = ctrset_offset(sp->set) + k;
|
size_t idx = ctrset_offset(sp->set) + k;
|
||||||
|
|
||||||
@@ -608,8 +609,8 @@ static int do_it(char *s)
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
memset(&start, 0, sizeof(start));
|
memset(&start, 0, sizeof(start));
|
||||||
rc = max_possible_cpus / sizeof(__u64);
|
rc = max_possible_cpus / sizeof(uint64_t);
|
||||||
start.cpumask = alloca(max_possible_cpus / sizeof(__u64));
|
start.cpumask = alloca(max_possible_cpus / sizeof(uint64_t));
|
||||||
memset(start.cpumask, 0, rc);
|
memset(start.cpumask, 0, rc);
|
||||||
parse_cpulist(s, &start);
|
parse_cpulist(s, &start);
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|||||||
+12
-11
@@ -13,6 +13,7 @@
|
|||||||
#ifndef LSHWC_H
|
#ifndef LSHWC_H
|
||||||
#define LSHWC_H
|
#define LSHWC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -58,27 +59,27 @@ enum {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
struct s390_hwctr_start { /* Set CPUs to operate on */
|
struct s390_hwctr_start { /* Set CPUs to operate on */
|
||||||
__u64 version; /* Version of interface */
|
uint64_t version; /* Version of interface */
|
||||||
__u64 data_bytes; /* # of bytes required */
|
uint64_t data_bytes; /* # of bytes required */
|
||||||
__u64 cpumask_len; /* Length of CPU mask in bytes */
|
uint64_t cpumask_len; /* Length of CPU mask in bytes */
|
||||||
__u64 *cpumask; /* Pointer to CPU mask */
|
uint64_t *cpumask; /* Pointer to CPU mask */
|
||||||
__u64 counter_sets; /* Bit mask of counter set to get */
|
uint64_t counter_sets; /* Bit mask of counter set to get */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct s390_hwctr_setdata { /* Counter set data */
|
struct s390_hwctr_setdata { /* Counter set data */
|
||||||
__u32 set; /* Counter set number */
|
uint32_t set; /* Counter set number */
|
||||||
__u32 no_cnts; /* # of counters stored in cv[] */
|
uint32_t no_cnts; /* # of counters stored in cv[] */
|
||||||
__u64 cv[0]; /* Counter values (variable length) */
|
uint64_t cv[0]; /* Counter values (variable length) */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct s390_hwctr_cpudata { /* Counter set data per CPU */
|
struct s390_hwctr_cpudata { /* Counter set data per CPU */
|
||||||
__u32 cpu_nr; /* Counter set number */
|
uint32_t cpu_nr; /* Counter set number */
|
||||||
__u32 no_sets; /* # of counters sets in data[] */
|
uint32_t no_sets; /* # of counters sets in data[] */
|
||||||
struct s390_hwctr_setdata data[0];
|
struct s390_hwctr_setdata data[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct s390_hwctr_read { /* Structure to get all ctr sets */
|
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];
|
struct s390_hwctr_cpudata data[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ static struct util_opt dump2tar_opts[] = {
|
|||||||
{
|
{
|
||||||
.option = { "file-timeout", required_argument, NULL, 'T' },
|
.option = { "file-timeout", required_argument, NULL, 'T' },
|
||||||
.desc = "Stop reading file after SEC seconds",
|
.desc = "Stop reading file after SEC seconds",
|
||||||
|
.argument = "SEC",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.option = { "file-max-size", required_argument, NULL, 'M' },
|
.option = { "file-max-size", required_argument, NULL, 'M' },
|
||||||
|
|||||||
+2
-1
@@ -69,6 +69,7 @@ check-dep-fuse:
|
|||||||
"fuse.h", \
|
"fuse.h", \
|
||||||
"fuse-devel or libfuse-dev", \
|
"fuse-devel or libfuse-dev", \
|
||||||
"HAVE_FUSE=0")
|
"HAVE_FUSE=0")
|
||||||
|
touch check-dep-fuse
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 hsavmcore \
|
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 hsavmcore \
|
||||||
@@ -81,6 +82,6 @@ install: all
|
|||||||
endif # HAVE_FUSE
|
endif # HAVE_FUSE
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f hsavmcore $(objects)
|
rm -f hsavmcore $(objects) check-dep-fuse
|
||||||
|
|
||||||
.PHONY: all install clean
|
.PHONY: all install clean
|
||||||
|
|||||||
@@ -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,
|
void util_hexdump_grp(FILE *fh, const char *tag, const void *data, int group,
|
||||||
int cnt, int indent);
|
int cnt, int indent);
|
||||||
void util_print_indented(const char *str, 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)
|
static inline void util_ptr_vec_free(void **ptr_vec, int count)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
#define TOOLS_LIBDIR STRINGIFY (S390_TOOLS_LIBDIR)
|
#define TOOLS_LIBDIR STRINGIFY (S390_TOOLS_LIBDIR)
|
||||||
#define TOOLS_SYSCONFDIR STRINGIFY (S390_TOOLS_SYSCONFDIR)
|
#define TOOLS_SYSCONFDIR STRINGIFY (S390_TOOLS_SYSCONFDIR)
|
||||||
#define TOOLS_BINDIR STRINGIFY (S390_TOOLS_BINDIR)
|
#define TOOLS_BINDIR STRINGIFY (S390_TOOLS_BINDIR)
|
||||||
|
#define TOOLS_DATADIR STRINGIFY (S390_TOOLS_DATADIR)
|
||||||
|
|
||||||
#define __noreturn __attribute__((noreturn))
|
#define __noreturn __attribute__((noreturn))
|
||||||
#define __packed __attribute__((packed))
|
#define __packed __attribute__((packed))
|
||||||
|
|||||||
+6
-4
@@ -47,6 +47,8 @@ detect-openssl-version.dep:
|
|||||||
echo "}" >> $(TMPFILE)
|
echo "}" >> $(TMPFILE)
|
||||||
mv $(TMPFILE) $@
|
mv $(TMPFILE) $@
|
||||||
|
|
||||||
|
CURL_CONFIG ?= curl-config
|
||||||
|
|
||||||
check-dep-libekmfweb: detect-openssl-version.dep
|
check-dep-libekmfweb: detect-openssl-version.dep
|
||||||
$(call check_dep, \
|
$(call check_dep, \
|
||||||
"libekmfweb", \
|
"libekmfweb", \
|
||||||
@@ -64,8 +66,8 @@ check-dep-libekmfweb: detect-openssl-version.dep
|
|||||||
"curl/curl.h", \
|
"curl/curl.h", \
|
||||||
"libcurl-devel", \
|
"libcurl-devel", \
|
||||||
"HAVE_LIBCURL=0" \
|
"HAVE_LIBCURL=0" \
|
||||||
`curl-config --cflags` `curl-config --libs`)
|
`$(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) --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
|
||||||
touch check-dep-libekmfweb
|
touch check-dep-libekmfweb
|
||||||
|
|
||||||
skip-libekmfweb-openssl:
|
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
|
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
|
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): ALL_CFLAGS += -fPIC `$(CURL_CONFIG) --cflags`
|
||||||
libekmfweb.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `curl-config --libs` -ldl
|
libekmfweb.so.$(VERSION): LDLIBS = -ljson-c -lcrypto -lssl `$(CURL_CONFIG) --libs` -ldl
|
||||||
libekmfweb.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libekmfweb.map \
|
libekmfweb.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libekmfweb.map \
|
||||||
-Wl,-z,defs,-Bsymbolic -Wl,-soname,libekmfweb.so.$(VERM)
|
-Wl,-z,defs,-Bsymbolic -Wl,-soname,libekmfweb.so.$(VERM)
|
||||||
libekmfweb.so.$(VERSION): ekmfweb.o utilities.o cca.o $(libs)
|
libekmfweb.so.$(VERSION): ekmfweb.o utilities.o cca.o $(libs)
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ detect-openssl-version.dep:
|
|||||||
echo "}" >> $(TMPFILE)
|
echo "}" >> $(TMPFILE)
|
||||||
mv $(TMPFILE) $@
|
mv $(TMPFILE) $@
|
||||||
|
|
||||||
|
CURL_CONFIG ?= curl-config
|
||||||
|
XML2_CONFIG ?= xml2-config
|
||||||
|
|
||||||
check-dep-libkmipclient: detect-openssl-version.dep
|
check-dep-libkmipclient: detect-openssl-version.dep
|
||||||
$(call check_dep, \
|
$(call check_dep, \
|
||||||
"libkmipclient", \
|
"libkmipclient", \
|
||||||
@@ -67,14 +70,14 @@ check-dep-libkmipclient: detect-openssl-version.dep
|
|||||||
"libxml/tree.h", \
|
"libxml/tree.h", \
|
||||||
"libxml2-devel", \
|
"libxml2-devel", \
|
||||||
"HAVE_LIBXML2=0", \
|
"HAVE_LIBXML2=0", \
|
||||||
`xml2-config --cflags` `xml2-config --libs`)
|
`$(XML2_CONFIG) --cflags` `$(XML2_CONFIG) --libs`)
|
||||||
$(call check_dep, \
|
$(call check_dep, \
|
||||||
"libkmipclient", \
|
"libkmipclient", \
|
||||||
"curl/curl.h", \
|
"curl/curl.h", \
|
||||||
"libcurl-devel", \
|
"libcurl-devel", \
|
||||||
"HAVE_LIBCURL=0" \
|
"HAVE_LIBCURL=0" \
|
||||||
`curl-config --cflags` `curl-config --libs`)
|
`$(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) --ssl-backends | grep OpenSSL >/dev/null 2>&1 || { echo "Error: libcurl is not built with the OpenSSL backend"; exit 1; }
|
||||||
touch check-dep-libkmipclient
|
touch check-dep-libkmipclient
|
||||||
|
|
||||||
skip-libkmipclient-openssl:
|
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
|
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
|
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): 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): LDLIBS = -ljson-c -lcrypto -lssl `$(XML2_CONFIG) --libs` `$(CURL_CONFIG) --libs`
|
||||||
libkmipclient.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libkmipclient.map \
|
libkmipclient.so.$(VERSION): ALL_LDFLAGS += -shared -Wl,--version-script=libkmipclient.map \
|
||||||
-Wl,-z,defs,-Bsymbolic -Wl,-soname,libkmipclient.so.$(VERM)
|
-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 \
|
libkmipclient.so.$(VERSION): kmip.o request.o response.o attribute.o key.o ttlv.o json.o \
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ int kmip_parse_hex(const char *str, bool has_prefix, unsigned char **val,
|
|||||||
{
|
{
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
BIGNUM *b = NULL;
|
BIGNUM *b = NULL;
|
||||||
int len, rc;
|
int len;
|
||||||
|
|
||||||
if (str == NULL)
|
if (str == NULL)
|
||||||
return -EINVAL;
|
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)
|
if (has_prefix && strncmp(str, "0x", 2) != 0)
|
||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
|
|
||||||
rc = BN_hex2bn(&b, str + (has_prefix ? 2 : 0));
|
len = BN_hex2bn(&b, str + (has_prefix ? 2 : 0));
|
||||||
if (rc <= 0)
|
if (len <= 0)
|
||||||
|
return -EBADMSG;
|
||||||
|
if (len < (int)strlen(str) - (has_prefix ? 2 : 0))
|
||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
|
|
||||||
len = BN_num_bytes(b);
|
len = len / 2 + (len % 2 > 0 ? 1 : 0);
|
||||||
buf = calloc(1, len);
|
buf = calloc(1, len);
|
||||||
if (buf == NULL) {
|
if (buf == NULL) {
|
||||||
BN_free(b);
|
BN_free(b);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BN_bn2bin(b, buf) != len) {
|
if (BN_bn2binpad(b, buf, len) != len) {
|
||||||
BN_free(b);
|
BN_free(b);
|
||||||
free(buf);
|
free(buf);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
* it under the terms of the MIT license. See LICENSE for details.
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "lib/util_base.h"
|
#include "lib/util_base.h"
|
||||||
@@ -91,3 +93,91 @@ void util_print_indented(const char *str, int indent)
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
free(desc_ptr);
|
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());
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
include ../common.mak
|
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
|
MAN_PAGES = dbginfo.sh.1 zfcpdbf.1 zipl-switch-to-blscfg.1
|
||||||
|
|
||||||
all:
|
all:
|
||||||
|
|||||||
+463
-494
File diff suppressed because it is too large
Load Diff
+40
-24
@@ -1,4 +1,4 @@
|
|||||||
.TH DBGINFO.SH 1 "April 2021" "s390-tools"
|
.TH DBGINFO.SH 1 "September 2021" "s390-tools"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
dbginfo.sh \- collect runtime, configuration and trace information
|
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
|
This script collects runtime, configuration and trace information that can
|
||||||
be used to debug a Linux on IBM Z instance.
|
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.
|
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
|
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
|
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. [hostname] indicates the hostname of the system the data was
|
||||||
collected from. The [processorid] is taken from the processor 0 and indicates
|
collected from. The [processorid] is taken from the processor 0 and indicates
|
||||||
@@ -32,7 +34,12 @@ Print version information, then exit.
|
|||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-d <DIRECTORY>\fP, \fB\-\-directory <DIRECTORY>\fP
|
\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
|
.SH FILES
|
||||||
A .tgz file of the form
|
A .tgz file of the form
|
||||||
@@ -56,48 +63,57 @@ Copyright IBM Corp. 2002, 2021
|
|||||||
.PP
|
.PP
|
||||||
Hardware platform = s390x
|
Hardware platform = s390x
|
||||||
.br
|
.br
|
||||||
Kernel version = 5.4.0 (5.4.0-70-generic)
|
|
||||||
.br
|
|
||||||
Runtime environment = z/VM
|
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
|
.PP
|
||||||
1 of 16: Collecting command output
|
1 of 18: Collecting sysfs
|
||||||
.PP
|
.PP
|
||||||
2 of 16: Collecting z/VM command output
|
2 of 18: Collecting procfs
|
||||||
.PP
|
.PP
|
||||||
3 of 16: Collecting procfs
|
3 of 18: Collecting config files
|
||||||
.PP
|
.PP
|
||||||
4 of 16: Collecting sysfs
|
4 of 18: Collecting command output
|
||||||
.PP
|
.PP
|
||||||
5 of 16: Collecting log files
|
5 of 18: Collecting hyptop for z/VM - 5s output
|
||||||
.PP
|
.PP
|
||||||
6 of 16: Collecting config files
|
6 of 18: Collecting z/VM output
|
||||||
.PP
|
.PP
|
||||||
7 of 16: Collecting osa oat output
|
7 of 18: Collecting network output
|
||||||
.PP
|
.PP
|
||||||
8 of 16: Collecting ethtool output
|
8 of 18: Collecting osa oat output
|
||||||
.PP
|
.PP
|
||||||
9 of 16: Collecting tc output
|
9 of 18: Collecting ethtool output
|
||||||
.PP
|
.PP
|
||||||
10 of 16: Collecting bridge output
|
10 of 18: Collecting Trafic Control output
|
||||||
.PP
|
.PP
|
||||||
11 of 16: Collecting OpenVSwitch output
|
11 of 18: Collecting bridge output
|
||||||
.PP
|
.PP
|
||||||
12a of 16: Collecting docker container
|
12 of 18: Skip OpenVSwitch: ovs-vsctl not available
|
||||||
12b of 16: Collecting docker network
|
|
||||||
.PP
|
.PP
|
||||||
13 of 16: Collecting nvme output
|
13 of 18: Skip KVM: no virsh command
|
||||||
.PP
|
.PP
|
||||||
14 of 16: Collecting KVM data
|
14 of 18: Collecting docker output
|
||||||
.PP
|
.PP
|
||||||
15 of 16: Postprocessing
|
15 of 18: Collecting NVME storage output
|
||||||
.PP
|
.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
|
.PP
|
||||||
Collected data was saved to:
|
Collected data was saved to:
|
||||||
.br
|
.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
|
.br
|
||||||
Review the collected data before sending to your service organization.
|
Please review all collected data before sending to your service organization.
|
||||||
.SH HINTS
|
.SH HINTS
|
||||||
Run the script with root authority.
|
Run the script with root authority.
|
||||||
.br
|
.br
|
||||||
|
|||||||
Executable
+1172
File diff suppressed because it is too large
Load Diff
+6
-7
@@ -887,14 +887,13 @@ sub print_scsi_common
|
|||||||
print "FCP rsp inf cod: 0x", substr($rec->[2], 44, 2), "\n";
|
print "FCP rsp inf cod: 0x", substr($rec->[2], 44, 2), "\n";
|
||||||
print "FCP rsp IU : ", payload_format(substr($rec->[2], 114, 48));
|
print "FCP rsp IU : ", payload_format(substr($rec->[2], 114, 48));
|
||||||
|
|
||||||
if (defined($pay_sns)) {
|
if ($payload_length && defined($pay_sns)) {
|
||||||
print "Sense len : ", $payload_length, "\n"
|
print "Sense len : ", $payload_length, "\n";
|
||||||
if ($payload_length);
|
print_payload($payload_length, $pay_sns, "Sense info");
|
||||||
print_payload($payload_length, $pay_sns, "Sense info")
|
} elsif ($payload_length) {
|
||||||
}
|
|
||||||
if (defined($pay_riu) && ($payload_length > FCP_RESP_WITH_EXT)) {
|
|
||||||
print "FCP rsp IU len : ", $payload_length, "\n";
|
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";
|
print "\n";
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -1863,7 +1863,6 @@ static void close_ur_device(struct vmur *info)
|
|||||||
}
|
}
|
||||||
cpcmd_cs(cmd, &response, &cprc, 0);
|
cpcmd_cs(cmd, &response, &cprc, 0);
|
||||||
memcpy(spoolid, &response[9], 4);
|
memcpy(spoolid, &response[9], 4);
|
||||||
free(response);
|
|
||||||
if (cprc == 439) {
|
if (cprc == 439) {
|
||||||
if (info->action == PUNCH)
|
if (info->action == PUNCH)
|
||||||
sprintf(cmd, "PURGE * PUN %s", spoolid);
|
sprintf(cmd, "PURGE * PUN %s", spoolid);
|
||||||
@@ -1872,7 +1871,10 @@ static void close_ur_device(struct vmur *info)
|
|||||||
cpcmd(cmd, NULL, NULL, 0);
|
cpcmd(cmd, NULL, NULL, 0);
|
||||||
ERR_EXIT("User %s spool fileid limit exceeded.\n"
|
ERR_EXIT("User %s spool fileid limit exceeded.\n"
|
||||||
, info->user);
|
, 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->rdr_specified) {
|
||||||
if (info->node_specified)
|
if (info->node_specified)
|
||||||
printf("Reader file with spoolid %s created and "
|
printf("Reader file with spoolid %s created and "
|
||||||
|
|||||||
+9
-1
@@ -463,7 +463,15 @@ static int fill_io_devid(struct util_rec *rec, char *path)
|
|||||||
util_scandir_free(de_vec, count);
|
util_scandir_free(de_vec, count);
|
||||||
return 1;
|
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);
|
fill_device_info(rec, NULL, NULL);
|
||||||
}
|
}
|
||||||
if (cmd.opt_uppercase)
|
if (cmd.opt_uppercase)
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ $(readlink -e $HOST_PATH)/-"
|
|||||||
|
|
||||||
read ONLINE < $ADAPTER_PATH/online
|
read ONLINE < $ADAPTER_PATH/online
|
||||||
read AVAILABILITY < $ADAPTER_PATH/availability
|
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
|
[ -r $ADAPTER_PATH/failed ] && read FAILED < $ADAPTER_PATH/failed
|
||||||
DEFUNCT=${ADAPTER_PATH%/*} # strip devbusid
|
DEFUNCT=${ADAPTER_PATH%/*} # strip devbusid
|
||||||
DEFUNCT=${DEFUNCT##*/} # basename
|
DEFUNCT=${DEFUNCT##*/} # basename
|
||||||
@@ -212,6 +213,8 @@ $(readlink -e $HOST_PATH)/-"
|
|||||||
HOSTMARKER=" defunct"
|
HOSTMARKER=" defunct"
|
||||||
elif [ "$ONLINE" = "0" ]; then
|
elif [ "$ONLINE" = "0" ]; then
|
||||||
HOSTMARKER=" offline"
|
HOSTMARKER=" offline"
|
||||||
|
elif [ "$PORT_STATE" = "Linkdown" ]; then
|
||||||
|
HOSTMARKER=" linkdown"
|
||||||
elif [ "$FAILED" = "1" ]; then
|
elif [ "$FAILED" = "1" ]; then
|
||||||
HOSTMARKER=" failed"
|
HOSTMARKER=" failed"
|
||||||
elif [ "$AVAILABILITY" != "good" ]; then
|
elif [ "$AVAILABILITY" != "good" ]; then
|
||||||
|
|||||||
+14
-2
@@ -115,6 +115,11 @@ Displays version information and exits.
|
|||||||
Here is an explanation of the columns displayed. Please note that some
|
Here is an explanation of the columns displayed. Please note that some
|
||||||
of the columns show up in verbose mode only.
|
of the columns show up in verbose mode only.
|
||||||
.TP
|
.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
|
.B TYPE and HWTYPE
|
||||||
The HWTYPE is a numeric value showing which type of hardware the zcrypt
|
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
|
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.
|
trigger this state with the --config-on and --config-off options.
|
||||||
.br
|
.br
|
||||||
lszcrypt shows 'online' when a card or queue is available for
|
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
|
is switched to (software) offline. If a card is 'deconfigured' via
|
||||||
HMC, SE or chzcrypt the field shows 'deconfig'.
|
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
|
.TP
|
||||||
.B REQUESTS
|
.B REQUESTS
|
||||||
This is the counter value of successful processed requests on card or queue
|
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.
|
enabled.
|
||||||
.br
|
.br
|
||||||
M - Accelerator card/queue with support for RSA ME with up to 4k key size.
|
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.
|
C - Accelerator card/queue with support for RSA CRT with up to 4k key size.
|
||||||
.br
|
.br
|
||||||
D - Card/queue is providing CCA functions (this is the CCA Coprocessor mode).
|
D - Card/queue is providing CCA functions (this is the CCA Coprocessor mode).
|
||||||
|
|||||||
+122
-68
@@ -22,6 +22,8 @@
|
|||||||
#include "lib/util_scandir.h"
|
#include "lib/util_scandir.h"
|
||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Private data
|
* Private data
|
||||||
*/
|
*/
|
||||||
@@ -331,13 +333,49 @@ static void show_capability(const char *id_str)
|
|||||||
printf("\n");
|
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
|
* Read subdevice default attributes
|
||||||
*/
|
*/
|
||||||
static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
|
static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
|
||||||
const char *sub_dev)
|
const char *sub_dev)
|
||||||
{
|
{
|
||||||
long value;
|
long config = -1, online = -1;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
unsigned long facility;
|
unsigned long facility;
|
||||||
|
|
||||||
@@ -346,24 +384,29 @@ static void read_subdev_rec_default(struct util_rec *rec, const char *grp_dev,
|
|||||||
else
|
else
|
||||||
util_rec_set(rec, "type", buf);
|
util_rec_set(rec, "type", buf);
|
||||||
|
|
||||||
if (util_path_is_readable("%s/%s/online", grp_dev, sub_dev)) {
|
if (util_path_is_readable("%s/%s/config", grp_dev, sub_dev))
|
||||||
util_file_read_l(&value, 10, "%s/%s/online", grp_dev, sub_dev);
|
util_file_read_l(&config, 10, "%s/%s/config", grp_dev, sub_dev);
|
||||||
if (value > 0)
|
if (util_path_is_readable("%s/%s/online", grp_dev, sub_dev))
|
||||||
util_rec_set(rec, "online", "online");
|
util_file_read_l(&online, 10, "%s/%s/online", grp_dev, sub_dev);
|
||||||
else {
|
|
||||||
/* device is offline, check config (if available) */
|
util_rec_set(rec, "status", "-");
|
||||||
if (util_path_is_readable("%s/%s/config", grp_dev, sub_dev)) {
|
if (config == 0) {
|
||||||
util_file_read_l(&value, 10, "%s/%s/config", grp_dev, sub_dev);
|
util_rec_set(rec, "status", "deconfig");
|
||||||
if (value > 0)
|
|
||||||
util_rec_set(rec, "online", "offline");
|
|
||||||
else
|
|
||||||
util_rec_set(rec, "online", "deconfig");
|
|
||||||
} else
|
|
||||||
util_rec_set(rec, "online", "offline");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
/* no online attribute */
|
if (online > 0)
|
||||||
util_rec_set(rec, "online", "-");
|
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);
|
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
|
else
|
||||||
util_rec_set(rec, "mode", "Unknown");
|
util_rec_set(rec, "mode", "Unknown");
|
||||||
|
|
||||||
util_file_read_line(buf, sizeof(buf), "%s/%s/request_count",
|
if (config == 0) {
|
||||||
grp_dev, sub_dev);
|
util_rec_set(rec, "requests", "-");
|
||||||
util_rec_set(rec, "requests", buf);
|
} 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,
|
static void read_subdev_rec_verbose(struct util_rec *rec, const char *grp_dev,
|
||||||
const char *sub_dev)
|
const char *sub_dev)
|
||||||
{
|
{
|
||||||
int i;
|
int i, drvinfo;
|
||||||
unsigned long facility;
|
unsigned long facility;
|
||||||
char buf[256], afile[PATH_MAX];
|
char buf[256];
|
||||||
long depth, pending1, pending2;
|
long depth, pending1, pending2, config = -1;
|
||||||
|
|
||||||
if (l.verbose == 0)
|
if (l.verbose == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
util_file_read_l(&pending1, 10, "%s/%s/pendingq_count",
|
drvinfo = read_driver(grp_dev, sub_dev, buf, sizeof(buf));
|
||||||
grp_dev, sub_dev);
|
util_rec_set(rec, "driver", drvinfo > 0 ? buf : "-no-driver-");
|
||||||
util_file_read_l(&pending2, 10, "%s/%s/requestq_count",
|
|
||||||
grp_dev, sub_dev);
|
if (util_path_is_readable("%s/config", grp_dev))
|
||||||
util_rec_set(rec, "pending", "%ld", pending1 + pending2);
|
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_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev);
|
||||||
util_rec_set(rec, "hwtype", buf);
|
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] = facility & fac_bits[i].mask ? fac_bits[i].c : '-';
|
||||||
buf[i] = '\0';
|
buf[i] = '\0';
|
||||||
util_rec_set(rec, "facility", buf);
|
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)
|
static void read_rec_default(struct util_rec *rec, const char *grp_dev)
|
||||||
{
|
{
|
||||||
long value;
|
long config = -1, online = -1;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
unsigned long facility;
|
unsigned long facility;
|
||||||
|
|
||||||
@@ -486,24 +535,27 @@ static void read_rec_default(struct util_rec *rec, const char *grp_dev)
|
|||||||
else
|
else
|
||||||
util_rec_set(rec, "mode", "Unknown");
|
util_rec_set(rec, "mode", "Unknown");
|
||||||
|
|
||||||
if (util_path_is_readable("%s/online", grp_dev)) {
|
if (util_path_is_readable("%s/config", grp_dev))
|
||||||
util_file_read_l(&value, 10, "%s/online", grp_dev);
|
util_file_read_l(&config, 10, "%s/config", grp_dev);
|
||||||
if (value > 0)
|
if (util_path_is_readable("%s/online", grp_dev))
|
||||||
util_rec_set(rec, "online", "online");
|
util_file_read_l(&online, 10, "%s/online", grp_dev);
|
||||||
else {
|
if (config == 0) {
|
||||||
if (util_path_is_readable("%s/config", grp_dev)) {
|
util_rec_set(rec, "status", "deconfig");
|
||||||
util_file_read_l(&value, 10, "%s/config", grp_dev);
|
} else {
|
||||||
if (value > 0)
|
if (online > 0)
|
||||||
util_rec_set(rec, "online", "offline");
|
util_rec_set(rec, "status", "online");
|
||||||
else
|
else if (online == 0)
|
||||||
util_rec_set(rec, "online", "deconfig");
|
util_rec_set(rec, "status", "offline");
|
||||||
} else
|
else
|
||||||
util_rec_set(rec, "online", "offline");
|
util_rec_set(rec, "status", "-");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
util_file_read_line(buf, sizeof(buf), "%s/request_count", grp_dev);
|
if (config == 0) {
|
||||||
util_rec_set(rec, "requests", buf);
|
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;
|
int i;
|
||||||
unsigned long facility;
|
unsigned long facility;
|
||||||
char buf[256], afile[PATH_MAX];
|
char buf[256];
|
||||||
long depth, pending1, pending2;
|
long depth, pending1, pending2, config = -1;
|
||||||
|
|
||||||
if (l.verbose == 0)
|
if (l.verbose == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
util_file_read_l(&pending1, 10, "%s/pendingq_count", grp_dev);
|
if (util_path_is_readable("%s/config", grp_dev))
|
||||||
util_file_read_l(&pending2, 10, "%s/requestq_count", grp_dev);
|
util_file_read_l(&config, 10, "%s/config", grp_dev);
|
||||||
util_rec_set(rec, "pending", "%ld", pending1 + pending2);
|
|
||||||
|
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_file_read_line(buf, sizeof(buf), "%s/hwtype", grp_dev);
|
||||||
util_rec_set(rec, "hwtype", buf);
|
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';
|
buf[i] = '\0';
|
||||||
util_rec_set(rec, "facility", buf);
|
util_rec_set(rec, "facility", buf);
|
||||||
|
|
||||||
snprintf(afile, sizeof(afile), "%s/driver", grp_dev);
|
i = read_driver(grp_dev, NULL, buf, sizeof(buf));
|
||||||
afile[sizeof(afile) - 1] = '\0';
|
util_rec_set(rec, "driver", i > 0 ? buf : "-no-driver-");
|
||||||
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-");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -581,10 +635,10 @@ out_free:
|
|||||||
*/
|
*/
|
||||||
static void define_rec_default(struct util_rec *rec)
|
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, "type", UTIL_REC_ALIGN_LEFT, 5, "TYPE");
|
||||||
util_rec_def(rec, "mode", UTIL_REC_ALIGN_LEFT, 11, "MODE");
|
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");
|
util_rec_def(rec, "requests", UTIL_REC_ALIGN_RIGHT, 8, "REQUESTS");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ all:
|
|||||||
install: all
|
install: all
|
||||||
$(MAKE) -C src install
|
$(MAKE) -C src install
|
||||||
$(MAKE) -C man install
|
$(MAKE) -C man install
|
||||||
|
$(MAKE) -C udev install
|
||||||
$(MAKE) -C dracut install
|
$(MAKE) -C dracut install
|
||||||
$(MAKE) -C initramfs install
|
$(MAKE) -C initramfs install
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ check() {
|
|||||||
[ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1
|
[ "$_arch" = "s390" -o "$_arch" = "s390x" ] || return 1
|
||||||
|
|
||||||
# Ensure that required tools are available
|
# 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
|
return 0
|
||||||
}
|
}
|
||||||
@@ -40,11 +40,14 @@ install() {
|
|||||||
local _tempfile
|
local _tempfile
|
||||||
|
|
||||||
# Ensure that required tools are available
|
# 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
|
# Hook to parse zdev kernel parameter
|
||||||
inst_hook cmdline 95 "$moddir/parse-zdev.sh"
|
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
|
# Obtain early + root device configuration
|
||||||
_tempfile=$(mktemp --tmpdir dracut-zdev.XXXXXX)
|
_tempfile=$(mktemp --tmpdir dracut-zdev.XXXXXX)
|
||||||
chzdev --export "$_tempfile" --persistent --by-path / --quiet \
|
chzdev --export "$_tempfile" --persistent --by-path / --quiet \
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
#
|
#
|
||||||
# Copyright IBM Corp. 2017
|
# Copyright IBM Corp. 2017,2021
|
||||||
#
|
#
|
||||||
# s390-tools is free software; you can redistribute it and/or modify
|
# s390-tools is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the MIT license. See LICENSE for details.
|
# it under the terms of the MIT license. See LICENSE for details.
|
||||||
@@ -15,11 +15,14 @@
|
|||||||
# where
|
# where
|
||||||
#
|
#
|
||||||
# no-auto: Indicates that firmware-provided I/O configuration data
|
# 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_fw_file="/sys/firmware/sclp_sd/config/data"
|
||||||
zdev_base_args="--force --yes --no-root-update --no-settle --auto-conf --quiet"
|
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
|
if [ -e "$zdev_fw_file" ] ; then
|
||||||
zdev_auto=1
|
zdev_auto=1
|
||||||
@@ -35,4 +38,22 @@ done
|
|||||||
|
|
||||||
if [ $zdev_auto -eq 1 ] ; then
|
if [ $zdev_auto -eq 1 ] ; then
|
||||||
chzdev --import "$zdev_fw_file" $zdev_base_args
|
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
|
fi
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ done
|
|||||||
copy_exec /sbin/chzdev
|
copy_exec /sbin/chzdev
|
||||||
copy_exec /sbin/lszdev
|
copy_exec /sbin/lszdev
|
||||||
copy_exec /sbin/vmcp
|
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)
|
_tempfile=$(mktemp --tmpdir initramfs-zdev.XXXXXX)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
#
|
#
|
||||||
# Copyright IBM Corp. 2017
|
# Copyright IBM Corp. 2017,2021
|
||||||
#
|
#
|
||||||
# s390-tools is free software; you can redistribute it and/or modify
|
# s390-tools is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the MIT license. See LICENSE for details.
|
# it under the terms of the MIT license. See LICENSE for details.
|
||||||
@@ -15,7 +15,9 @@
|
|||||||
# where
|
# where
|
||||||
#
|
#
|
||||||
# no-auto: Indicates that firmware-provided I/O configuration data
|
# 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"
|
PREREQ="udev"
|
||||||
@@ -36,6 +38,7 @@ esac
|
|||||||
|
|
||||||
zdev_fw_file="/sys/firmware/sclp_sd/config/data"
|
zdev_fw_file="/sys/firmware/sclp_sd/config/data"
|
||||||
zdev_base_args="--force --yes --no-root-update --no-settle --auto-conf --quiet"
|
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
|
if [ -e "$zdev_fw_file" ] ; then
|
||||||
zdev_auto=1
|
zdev_auto=1
|
||||||
@@ -64,4 +67,22 @@ if [ $zdev_auto -eq 1 ] ; then
|
|||||||
udevadm trigger --type=subsystems --action=add
|
udevadm trigger --type=subsystems --action=add
|
||||||
udevadm trigger --action=add
|
udevadm trigger --action=add
|
||||||
udevadm settle || true
|
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
|
fi
|
||||||
|
|||||||
+4
-3
@@ -73,7 +73,7 @@ lszdev_objects += lcs.o lcs_auto.o
|
|||||||
# Generic CCW devtype
|
# Generic CCW devtype
|
||||||
lszdev_objects += generic_ccw.o
|
lszdev_objects += generic_ccw.o
|
||||||
|
|
||||||
all: chzdev lszdev
|
all: chzdev lszdev zdev_id
|
||||||
|
|
||||||
chzdev_usage.c: chzdev_usage.txt
|
chzdev_usage.c: chzdev_usage.txt
|
||||||
$(SED) $< >$@ -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/g' \
|
$(SED) $< >$@ -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/g' \
|
||||||
@@ -95,16 +95,17 @@ libs = $(rootdir)/libutil/libutil.a
|
|||||||
chzdev: $(chzdev_objects) $(libs)
|
chzdev: $(chzdev_objects) $(libs)
|
||||||
lszdev: $(lszdev_objects) $(libs)
|
lszdev: $(lszdev_objects) $(libs)
|
||||||
|
|
||||||
install: chzdev
|
install: chzdev zdev_id
|
||||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
||||||
$(INSTALL) -c chzdev $(DESTDIR)$(BINDIR)
|
$(INSTALL) -c chzdev $(DESTDIR)$(BINDIR)
|
||||||
$(INSTALL) -c lszdev $(DESTDIR)$(BINDIR)
|
$(INSTALL) -c lszdev $(DESTDIR)$(BINDIR)
|
||||||
|
$(INSTALL) -c zdev_id $(DESTDIR)$(TOOLS_LIBDIR)
|
||||||
ifeq ($(HAVE_DRACUT),1)
|
ifeq ($(HAVE_DRACUT),1)
|
||||||
$(INSTALL) -m 755 zdev-root-update.dracut \
|
$(INSTALL) -m 755 zdev-root-update.dracut \
|
||||||
$(DESTDIR)$(TOOLS_LIBDIR)/zdev-root-update
|
$(DESTDIR)$(TOOLS_LIBDIR)/zdev-root-update
|
||||||
endif
|
endif
|
||||||
|
|
||||||
clean:
|
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
|
.PHONY: all install clean
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
@@ -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)/
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
include ../common.mak
|
include ../common.mak
|
||||||
|
|
||||||
|
ALL_CFLAGS += -Wno-address-of-packed-member
|
||||||
|
|
||||||
#
|
#
|
||||||
# HAVE_FUSE: Allow to build zgetdump without mount support
|
# HAVE_FUSE: Allow to build zgetdump without mount support
|
||||||
#
|
#
|
||||||
|
|||||||
+9
-3
@@ -37,9 +37,10 @@ static int check_addr_max(struct df_s390_hdr *hdr, u64 addr_max)
|
|||||||
/*
|
/*
|
||||||
* Convert lowcore information into internal CPU representation
|
* 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;
|
unsigned int i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (hdr->version < 5 && hdr->magic == DF_S390_MAGIC) {
|
if (hdr->version < 5 && hdr->magic == DF_S390_MAGIC) {
|
||||||
/* No Prefix registers in header */
|
/* 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);
|
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < hdr->cpu_cnt; i++)
|
for (i = 0; i < hdr->cpu_cnt; i++) {
|
||||||
dfi_cpu_add_from_lc(hdr->lc_vec[i]);
|
rc = dfi_cpu_add_from_lc(hdr->lc_vec[i]);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
+1
-1
@@ -127,7 +127,7 @@ struct df_s390_dumper {
|
|||||||
*/
|
*/
|
||||||
extern void df_s390_hdr_add(struct df_s390_hdr *hdr);
|
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_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 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,
|
extern void df_s390_dumper_read(struct zg_fh *fh, int32_t blk_size,
|
||||||
struct df_s390_dumper *dumper);
|
struct df_s390_dumper *dumper);
|
||||||
|
|||||||
+56
-9
@@ -10,6 +10,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "lib/util_log.h"
|
||||||
#include "zgetdump.h"
|
#include "zgetdump.h"
|
||||||
|
|
||||||
#define TIME_FMT_STR "%a, %d %b %Y %H:%M:%S %z"
|
#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
|
* Print each memory chunk if verbose specified
|
||||||
*/
|
*/
|
||||||
if (g.opts.verbose_specified) {
|
if (g.opts.verbose) {
|
||||||
dfi_mem_chunk_iterate(mem_chunk) {
|
dfi_mem_chunk_iterate(mem_chunk) {
|
||||||
zero_str = "";
|
zero_str = "";
|
||||||
if (mem_chunk->read_fn == dfi_mem_chunk_read_zero)
|
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;
|
struct dfi_mem_chunk *mem_chunk;
|
||||||
u64 addr_end = addr + len;
|
u64 addr_end = addr + len;
|
||||||
|
|
||||||
|
/* check for unsigned wrap */
|
||||||
|
if (addr_end < addr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
mem_chunk = dfi_mem_chunk_find(addr);
|
mem_chunk = dfi_mem_chunk_find(addr);
|
||||||
if (!mem_chunk)
|
if (!mem_chunk)
|
||||||
@@ -236,7 +242,7 @@ void dfi_info_print(void)
|
|||||||
STDERR("General dump info:\n");
|
STDERR("General dump info:\n");
|
||||||
STDERR(" Dump format........: %s\n", l.dfi->name);
|
STDERR(" Dump format........: %s\n", l.dfi->name);
|
||||||
if (l.attr.dfi_version)
|
if (l.attr.dfi_version)
|
||||||
STDERR(" Version............: %d\n", *l.attr.dfi_version);
|
STDERR(" Version............: %u\n", *l.attr.dfi_version);
|
||||||
date_print();
|
date_print();
|
||||||
if (l.attr.dump_method)
|
if (l.attr.dump_method)
|
||||||
STDERR(" Dump method........: %s\n", 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);
|
STDERR(" UTS kernel version.: %s\n", l.attr.utsname->version);
|
||||||
}
|
}
|
||||||
if (l.attr.vol_nr)
|
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)
|
if (l.attr.build_arch)
|
||||||
STDERR(" Build arch.........: %s\n",
|
STDERR(" Build arch.........: %s\n",
|
||||||
dfi_arch_str(*l.attr.build_arch));
|
dfi_arch_str(*l.attr.build_arch));
|
||||||
STDERR(" System arch........: %s\n", dfi_arch_str(l.arch));
|
STDERR(" System arch........: %s\n", dfi_arch_str(l.arch));
|
||||||
if (l.cpus.cnt)
|
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)
|
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())
|
if (dfi_mem_range())
|
||||||
STDERR(" Dump memory range..: %lld MB\n",
|
STDERR(" Dump memory range..: %lld MB\n",
|
||||||
TO_MIB(dfi_mem_range()));
|
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;
|
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;
|
return mem->chunk_cache;
|
||||||
util_list_iterate(&mem->chunk_list, mem_chunk) {
|
util_list_iterate(&mem->chunk_list, mem_chunk) {
|
||||||
if (mem_chunk_has_addr(mem_chunk, addr)) {
|
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_read_fn read_fn,
|
||||||
dfi_mem_chunk_free_fn free_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)
|
if (size == 0)
|
||||||
return;
|
return;
|
||||||
mem_chunk_create(&l.mem_virt, start, size, data, read_fn, free_fn);
|
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,
|
dfi_mem_chunk_free_fn free_fn,
|
||||||
u32 volnr)
|
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)
|
if (size == 0)
|
||||||
return;
|
return;
|
||||||
mem_chunk_create(&l.mem_phys, start, size, data, read_fn, free_fn);
|
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)
|
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);
|
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)
|
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);
|
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
|
* Note: When this function is called, the memory chunks have to be already
|
||||||
* defined by the DFI dump specific code.
|
* 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();
|
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) {
|
if (l.arch == DFI_ARCH_32) {
|
||||||
struct dfi_cpu_32 cpu_32;
|
struct dfi_cpu_32 cpu_32;
|
||||||
struct dfi_lowcore_32 lc;
|
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);
|
lc2cpu_32(&cpu_32, &lc);
|
||||||
cpu_32_to_64(cpu, &cpu_32);
|
cpu_32_to_64(cpu, &cpu_32);
|
||||||
} else {
|
} else {
|
||||||
struct dfi_lowcore_64 lc;
|
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);
|
lc2cpu_64(cpu, &lc);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -943,6 +967,7 @@ void dfi_cpu_add_from_lc(u32 lc_addr)
|
|||||||
ABORT("dfi_cpu_add_from_lc() called for CONTENT_NONE");
|
ABORT("dfi_cpu_add_from_lc() called for CONTENT_NONE");
|
||||||
}
|
}
|
||||||
dfi_cpu_add(cpu);
|
dfi_cpu_add(cpu);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1074,6 +1099,12 @@ static void kdump_init(void)
|
|||||||
{
|
{
|
||||||
unsigned long base, size;
|
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(0x10418, &base, sizeof(base));
|
||||||
dfi_mem_phys_read(0x10420, &size, sizeof(size));
|
dfi_mem_phys_read(0x10420, &size, sizeof(size));
|
||||||
if (base == 0 || size == 0)
|
if (base == 0 || size == 0)
|
||||||
@@ -1084,6 +1115,9 @@ static void kdump_init(void)
|
|||||||
return;
|
return;
|
||||||
l.kdump_base = base;
|
l.kdump_base = base;
|
||||||
l.kdump_size = size;
|
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
|
* For dumped kdump and user has selected "prod" we swap
|
||||||
* the crashkernel memory with old memory. If user selected "kdump",
|
* the crashkernel memory with old memory. If user selected "kdump",
|
||||||
@@ -1146,6 +1180,8 @@ static void utsname_init(void)
|
|||||||
unsigned long ptr;
|
unsigned long ptr;
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_TRACE, "DFI utsname initialization\n");
|
||||||
|
|
||||||
if (dfi_vmcoreinfo_symbol(&ptr, "init_uts_ns"))
|
if (dfi_vmcoreinfo_symbol(&ptr, "init_uts_ns"))
|
||||||
return;
|
return;
|
||||||
if (dfi_mem_read_rc(ptr, buf, sizeof(buf)))
|
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)
|
if (strncmp(utsname->sysname, "Linux", sizeof(utsname->version)) != 0)
|
||||||
return;
|
return;
|
||||||
dfi_attr_utsname_set(utsname);
|
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;
|
u64 magic;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_TRACE, "DFI livedump initialization\n");
|
||||||
|
|
||||||
if (dfi_mem_read_rc(0, &magic, sizeof(magic)))
|
if (dfi_mem_read_rc(0, &magic, sizeof(magic)))
|
||||||
return;
|
return;
|
||||||
if (magic == dfi_live_dump_magic)
|
if (magic == dfi_live_dump_magic)
|
||||||
@@ -1201,12 +1241,15 @@ int dfi_init(void)
|
|||||||
struct dfi *dfi;
|
struct dfi *dfi;
|
||||||
int i = 0, rc;
|
int i = 0, rc;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_TRACE, "DFI initialization\n");
|
||||||
|
|
||||||
l.arch = DFI_ARCH_UNKNOWN;
|
l.arch = DFI_ARCH_UNKNOWN;
|
||||||
mem_init(&l.mem_virt);
|
mem_init(&l.mem_virt);
|
||||||
mem_init(&l.mem_phys);
|
mem_init(&l.mem_phys);
|
||||||
attr_init();
|
attr_init();
|
||||||
dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
|
dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
|
||||||
while ((dfi = dfi_vec[i])) {
|
while ((dfi = dfi_vec[i])) {
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "DFI trying %s\n", dfi->name);
|
||||||
l.dfi = dfi;
|
l.dfi = dfi;
|
||||||
g.fh = dfi_dump_open(g.opts.device);
|
g.fh = dfi_dump_open(g.opts.device);
|
||||||
rc = dfi->init();
|
rc = dfi->init();
|
||||||
@@ -1218,6 +1261,8 @@ int dfi_init(void)
|
|||||||
utsname_init();
|
utsname_init();
|
||||||
livedump_init();
|
livedump_init();
|
||||||
}
|
}
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "DFI %s returned with rc %d\n",
|
||||||
|
dfi->name, rc);
|
||||||
if (rc == 0 || rc == -EINVAL)
|
if (rc == 0 || rc == -EINVAL)
|
||||||
return rc;
|
return rc;
|
||||||
zg_close(g.fh);
|
zg_close(g.fh);
|
||||||
@@ -1231,6 +1276,8 @@ int dfi_init(void)
|
|||||||
*/
|
*/
|
||||||
void dfi_exit(void)
|
void dfi_exit(void)
|
||||||
{
|
{
|
||||||
|
util_log_print(UTIL_LOG_TRACE, "DFI exit\n");
|
||||||
|
|
||||||
if (l.dfi && l.dfi->exit)
|
if (l.dfi && l.dfi->exit)
|
||||||
l.dfi->exit();
|
l.dfi->exit();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -142,7 +142,7 @@ extern struct dfi_cpu *dfi_cpu(unsigned int cpu_nr);
|
|||||||
extern void dfi_cpu_add(struct dfi_cpu *cpu);
|
extern void dfi_cpu_add(struct dfi_cpu *cpu);
|
||||||
extern unsigned int dfi_cpu_cnt(void);
|
extern unsigned int dfi_cpu_cnt(void);
|
||||||
extern enum dfi_cpu_content dfi_cpu_content(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)
|
#define DFI_VX_SA_SIZE (32 * 16)
|
||||||
extern int dfi_cpu_lc_has_vx_sa(void *lc);
|
extern int dfi_cpu_lc_has_vx_sa(void *lc);
|
||||||
|
|||||||
+67
-41
@@ -16,6 +16,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/util_libc.h"
|
||||||
|
#include "lib/util_log.h"
|
||||||
#include "zgetdump.h"
|
#include "zgetdump.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -37,56 +39,68 @@ static int pt_load_add(Elf64_Phdr *phdr)
|
|||||||
{
|
{
|
||||||
u64 *off_ptr;
|
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) {
|
if (phdr->p_paddr != phdr->p_vaddr) {
|
||||||
phdr->p_paddr = phdr->p_vaddr;
|
phdr->p_paddr = phdr->p_vaddr;
|
||||||
STDERR("Dump file \"%s\" is a user space core dump\n",
|
STDERR("Dump file \"%s\" is a user space core dump\n",
|
||||||
g.opts.device);
|
g.opts.device);
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
if (phdr->p_memsz == 0)
|
||||||
|
return -EINVAL;
|
||||||
if (phdr->p_offset + phdr->p_filesz > zg_size(g.fh))
|
if (phdr->p_offset + phdr->p_filesz > zg_size(g.fh))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (phdr->p_filesz == 0) {
|
if (phdr->p_filesz > phdr->p_memsz)
|
||||||
/* Add zero memory chunk */
|
return -EINVAL;
|
||||||
dfi_mem_chunk_add(phdr->p_paddr, phdr->p_memsz, NULL,
|
if (phdr->p_filesz > 0) {
|
||||||
dfi_mem_chunk_read_zero, NULL);
|
|
||||||
} else {
|
|
||||||
off_ptr = zg_alloc(sizeof(*off_ptr));
|
off_ptr = zg_alloc(sizeof(*off_ptr));
|
||||||
*off_ptr = phdr->p_offset;
|
*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);
|
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;
|
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
|
* 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);
|
ssize_t nread;
|
||||||
char tmp_buf[buf_len];
|
|
||||||
|
|
||||||
nt_name_skip(note);
|
/* We cannot read more than the current note provides */
|
||||||
if (zg_read(g.fh, tmp_buf, buf_len, ZG_CHECK_ERR) != buf_len)
|
if (note->n_descsz < buf_len)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (buf)
|
/* Skip note's name and position file at note's descriptor */
|
||||||
memcpy(buf, tmp_buf, note->n_descsz);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Skip note
|
* 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 dfi_cpu *cpu = dfi_cpu_alloc();
|
||||||
struct nt_prstatus_64 nt_prstatus;
|
struct nt_prstatus_64 nt_prstatus;
|
||||||
|
|
||||||
if (nt_read(note, &nt_prstatus))
|
if (nt_read(note, &nt_prstatus, sizeof(nt_prstatus)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
memcpy(cpu->gprs, &nt_prstatus.gprs, sizeof(cpu->gprs));
|
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;
|
struct nt_fpregset_64 nt_fpregset;
|
||||||
|
|
||||||
check_cpu(cpu, "FPREGSET");
|
check_cpu(cpu, "FPREGSET");
|
||||||
if (nt_read(note, &nt_fpregset))
|
if (nt_read(note, &nt_fpregset, sizeof(nt_fpregset)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
memcpy(&cpu->fpc, &nt_fpregset.fpc, sizeof(cpu->fpc));
|
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)
|
static int nt_s390_timer_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||||
{
|
{
|
||||||
check_cpu(cpu, "S390_TIMER");
|
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)
|
static int nt_s390_todcmp_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||||
{
|
{
|
||||||
check_cpu(cpu, "S390_TODCMP");
|
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)
|
static int nt_s390_todpreg_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||||
{
|
{
|
||||||
check_cpu(cpu, "S390_TODPREG");
|
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)
|
static int nt_s390_ctrs_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||||
{
|
{
|
||||||
check_cpu(cpu, "S390_CTRS");
|
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)
|
static int nt_s390_prefix_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||||
{
|
{
|
||||||
check_cpu(cpu, "S390_PREFIX");
|
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)
|
static int nt_s390_vxrs_low_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||||
{
|
{
|
||||||
check_cpu(cpu, "S390_VXRS_LOW");
|
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)
|
static int nt_s390_vxrs_high_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||||
{
|
{
|
||||||
check_cpu(cpu, "S390_VXRS_HIGH");
|
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)
|
static int pt_notes_add(Elf64_Phdr *phdr)
|
||||||
{
|
{
|
||||||
u64 start_off = zg_tell(g.fh, ZG_CHECK);
|
|
||||||
struct dfi_cpu *cpu_current = NULL;
|
struct dfi_cpu *cpu_current = NULL;
|
||||||
u64 notes_start_off;
|
u64 notes_start_off;
|
||||||
Elf64_Nhdr note;
|
Elf64_Nhdr note;
|
||||||
@@ -214,6 +227,8 @@ static int pt_notes_add(Elf64_Phdr *phdr)
|
|||||||
rc = zg_read(g.fh, ¬e, sizeof(note), ZG_CHECK_ERR);
|
rc = zg_read(g.fh, ¬e, sizeof(note), ZG_CHECK_ERR);
|
||||||
if (rc != sizeof(note))
|
if (rc != sizeof(note))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "DFI ELF n_type 0x%x\n",
|
||||||
|
note.n_type);
|
||||||
switch (note.n_type) {
|
switch (note.n_type) {
|
||||||
case NT_PRSTATUS:
|
case NT_PRSTATUS:
|
||||||
cpu_current = nt_prstatus_read(¬e);
|
cpu_current = nt_prstatus_read(¬e);
|
||||||
@@ -255,12 +270,10 @@ static int pt_notes_add(Elf64_Phdr *phdr)
|
|||||||
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
|
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (nt_skip(¬e))
|
nt_skip(¬e);
|
||||||
return -EINVAL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
zg_seek(g.fh, start_off, ZG_CHECK);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,9 +300,11 @@ static int read_elf_hdr(Elf64_Ehdr *ehdr)
|
|||||||
static int dfi_elf_init(void)
|
static int dfi_elf_init(void)
|
||||||
{
|
{
|
||||||
Elf64_Ehdr ehdr;
|
Elf64_Ehdr ehdr;
|
||||||
Elf64_Phdr phdr;
|
Elf64_Phdr *phdr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "DFI ELF initialization\n");
|
||||||
|
|
||||||
if (read_elf_hdr(&ehdr) != 0)
|
if (read_elf_hdr(&ehdr) != 0)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
@@ -297,21 +312,32 @@ static int dfi_elf_init(void)
|
|||||||
dfi_arch_set(DFI_ARCH_64);
|
dfi_arch_set(DFI_ARCH_64);
|
||||||
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
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++) {
|
for (i = 0; i < ehdr.e_phnum; i++) {
|
||||||
zg_read(g.fh, &phdr, sizeof(phdr), ZG_CHECK);
|
util_log_print(UTIL_LOG_DEBUG, "DFI ELF p_type[%d] 0x%lx\n",
|
||||||
switch (phdr.p_type) {
|
i, phdr[i].p_type);
|
||||||
|
switch (phdr[i].p_type) {
|
||||||
case PT_LOAD:
|
case PT_LOAD:
|
||||||
if (pt_load_add(&phdr))
|
if (pt_load_add(&phdr[i])) {
|
||||||
|
free(phdr);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PT_NOTE:
|
case PT_NOTE:
|
||||||
if (pt_notes_add(&phdr))
|
if (pt_notes_add(&phdr[i])) {
|
||||||
|
free(phdr);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
free(phdr);
|
||||||
|
|
||||||
dfi_attr_version_set(ehdr.e_ident[EI_VERSION]);
|
dfi_attr_version_set(ehdr.e_ident[EI_VERSION]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-5
@@ -267,19 +267,25 @@ static int mem_init(void)
|
|||||||
/*
|
/*
|
||||||
* Initialize CPU information
|
* Initialize CPU information
|
||||||
*/
|
*/
|
||||||
static void cpu_init(void)
|
static int cpu_init(void)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (l.hdr_asm.magic != DF_LKCD_MAGIC_ASM) {
|
if (l.hdr_asm.magic != DF_LKCD_MAGIC_ASM) {
|
||||||
/* Old LKCD dump without asm header */
|
/* Old LKCD dump without asm header */
|
||||||
dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
|
dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
||||||
for (i = 0; i < l.hdr_asm.cpu_cnt; i++)
|
for (i = 0; i < l.hdr_asm.cpu_cnt; i++) {
|
||||||
dfi_cpu_add_from_lc(l.hdr_asm.lc_vec[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;
|
return -ENODEV;
|
||||||
if (mem_init() != 0)
|
if (mem_init() != 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
cpu_init();
|
if (cpu_init() != 0)
|
||||||
|
return -EINVAL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+29
-13
@@ -18,6 +18,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "lib/util_log.h"
|
||||||
#include "zgetdump.h"
|
#include "zgetdump.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -68,6 +69,9 @@ static int read_s390_hdr(void)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
if (l.hdr.magic != magic_number)
|
if (l.hdr.magic != magic_number)
|
||||||
return -ENODEV;
|
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);
|
df_s390_hdr_add(&l.hdr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -95,6 +99,9 @@ static int mem_chunks_add(void)
|
|||||||
{
|
{
|
||||||
u64 rc;
|
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 */
|
/* Single memory chunk for non-extended dump format */
|
||||||
dfi_mem_chunk_add(0, l.hdr.mem_size, NULL,
|
dfi_mem_chunk_add(0, l.hdr.mem_size, NULL,
|
||||||
dfi_s390_mem_chunk_read,
|
dfi_s390_mem_chunk_read,
|
||||||
@@ -112,39 +119,43 @@ static int mem_chunks_add(void)
|
|||||||
*/
|
*/
|
||||||
static int mem_chunks_add_ext(void)
|
static int mem_chunks_add_ext(void)
|
||||||
{
|
{
|
||||||
struct df_s390_dump_segm_hdr dump_segm;
|
struct df_s390_dump_segm_hdr dump_segm = { 0 };
|
||||||
u64 rc, *off_ptr, old = 0, dump_size = 0;
|
u64 rc, off, old = 0, dump_size = 0;
|
||||||
|
|
||||||
off_ptr = zg_alloc(sizeof(*off_ptr));
|
off = zg_seek(g.fh, DF_S390_HDR_SIZE, ZG_CHECK_NONE);
|
||||||
*off_ptr = zg_seek(g.fh, DF_S390_HDR_SIZE, ZG_CHECK_NONE);
|
if (off != DF_S390_HDR_SIZE)
|
||||||
if (*off_ptr != DF_S390_HDR_SIZE)
|
|
||||||
return -EINVAL;
|
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);
|
rc = zg_read(g.fh, &dump_segm, PAGE_SIZE, ZG_CHECK_ERR);
|
||||||
if (rc != PAGE_SIZE)
|
if (rc != PAGE_SIZE)
|
||||||
return -EINVAL;
|
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 */
|
/* Add zero memory chunk */
|
||||||
dfi_mem_chunk_add(old, dump_segm.start - old, NULL,
|
dfi_mem_chunk_add(old, dump_segm.start - old, NULL,
|
||||||
dfi_mem_chunk_read_zero, NULL);
|
dfi_mem_chunk_read_zero, NULL);
|
||||||
/* Add memory chunk for a dump segment */
|
/* 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_mem_chunk_add(dump_segm.start, dump_segm.len, off_ptr,
|
||||||
dfi_s390_ext_mem_chunk_read, zg_free);
|
dfi_s390_ext_mem_chunk_read, zg_free);
|
||||||
|
off_ptr = NULL;
|
||||||
old = dump_segm.start + dump_segm.len;
|
old = dump_segm.start + dump_segm.len;
|
||||||
dump_size += dump_segm.len;
|
dump_size += dump_segm.len;
|
||||||
off_ptr = zg_alloc(sizeof(*off_ptr));
|
off = zg_seek_cur(g.fh, dump_segm.len, ZG_CHECK_NONE);
|
||||||
*off_ptr = zg_seek_cur(g.fh, dump_segm.len, ZG_CHECK_NONE);
|
|
||||||
if (dump_segm.stop_marker)
|
if (dump_segm.stop_marker)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* Check if the last dump segment found */
|
||||||
|
if (!dump_segm.stop_marker)
|
||||||
|
return -EINVAL;
|
||||||
/* Add zero memory chunk at the end */
|
/* Add zero memory chunk at the end */
|
||||||
dfi_mem_chunk_add(old, l.hdr.mem_size - old, NULL,
|
dfi_mem_chunk_add(old, l.hdr.mem_size - old, NULL,
|
||||||
dfi_mem_chunk_read_zero, NULL);
|
dfi_mem_chunk_read_zero, NULL);
|
||||||
/* Set the actual size of the dump file */
|
/* Set the actual size of the dump file */
|
||||||
dfi_attr_file_size_set(dump_size);
|
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 */
|
/* Read and verify the end marker */
|
||||||
return read_s390_em();
|
return read_s390_em();
|
||||||
}
|
}
|
||||||
@@ -156,6 +167,9 @@ int dfi_s390_init_gen(bool extended)
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_DEBUG, "DFI S390 %sinitialization\n",
|
||||||
|
extended ? "extended " : "");
|
||||||
|
|
||||||
l.extended = extended;
|
l.extended = extended;
|
||||||
if (read_s390_hdr() != 0)
|
if (read_s390_hdr() != 0)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@@ -165,7 +179,9 @@ int dfi_s390_init_gen(bool extended)
|
|||||||
rc = mem_chunks_add_ext();
|
rc = mem_chunks_add_ext();
|
||||||
if (rc)
|
if (rc)
|
||||||
return 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);
|
zg_seek(g.fh, sizeof(l.hdr), ZG_CHECK);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -574,6 +574,8 @@ static void set_magic_numbers(void)
|
|||||||
*/
|
*/
|
||||||
int dfi_s390mv_init_gen(bool extended)
|
int dfi_s390mv_init_gen(bool extended)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
l.extended = extended;
|
l.extended = extended;
|
||||||
set_magic_numbers();
|
set_magic_numbers();
|
||||||
if (open_dump() != 0)
|
if (open_dump() != 0)
|
||||||
@@ -590,7 +592,9 @@ int dfi_s390mv_init_gen(bool extended)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (l.dump_incomplete)
|
if (l.dump_incomplete)
|
||||||
return -EINVAL;
|
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);
|
df_s390_em_add(&l.em);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,7 +166,9 @@ static int vol_init(void)
|
|||||||
|
|
||||||
/* Init memory read & CPU info */
|
/* Init memory read & CPU info */
|
||||||
mem_read_init();
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-2
@@ -12,6 +12,7 @@
|
|||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
|
|
||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
#include "lib/util_log.h"
|
||||||
|
|
||||||
#include "zgetdump.h"
|
#include "zgetdump.h"
|
||||||
|
|
||||||
@@ -62,7 +63,10 @@ static struct os_info *os_info_get(void)
|
|||||||
static struct os_info os_info;
|
static struct os_info os_info;
|
||||||
unsigned long addr;
|
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)
|
if (addr % 0x1000)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (dfi_mem_read_rc(addr, &os_info, sizeof(os_info)))
|
if (dfi_mem_read_rc(addr, &os_info, sizeof(os_info)))
|
||||||
@@ -83,17 +87,23 @@ void dfi_vmcoreinfo_init(void)
|
|||||||
Elf64_Nhdr note;
|
Elf64_Nhdr note;
|
||||||
char str[128];
|
char str[128];
|
||||||
|
|
||||||
|
util_log_print(UTIL_LOG_TRACE, "DFI vmcoreinfo initialization\n");
|
||||||
|
|
||||||
l.os_info = os_info_get();
|
l.os_info = os_info_get();
|
||||||
|
|
||||||
if (l.os_info && l.os_info->vmcoreinfo_size) {
|
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;
|
addr = l.os_info->vmcoreinfo_addr;
|
||||||
size = l.os_info->vmcoreinfo_size;
|
size = l.os_info->vmcoreinfo_size;
|
||||||
} else {
|
} 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)
|
if (addr == 0)
|
||||||
return;
|
return;
|
||||||
if (dfi_mem_read_rc(addr, ¬e, sizeof(note)))
|
if (dfi_mem_read_rc(addr, ¬e, sizeof(note)))
|
||||||
return;
|
return;
|
||||||
|
if (note.n_namesz == 0 || note.n_namesz > sizeof(str))
|
||||||
|
return;
|
||||||
memset(str, 0, sizeof(str));
|
memset(str, 0, sizeof(str));
|
||||||
if (dfi_mem_read_rc(addr + sizeof(note), str, note.n_namesz))
|
if (dfi_mem_read_rc(addr + sizeof(note), str, note.n_namesz))
|
||||||
return;
|
return;
|
||||||
@@ -102,6 +112,9 @@ void dfi_vmcoreinfo_init(void)
|
|||||||
size = note.n_descsz;
|
size = note.n_descsz;
|
||||||
addr += 24;
|
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);
|
l.vmcoreinfo = zg_alloc(size + 1);
|
||||||
if (dfi_mem_read_rc(addr, l.vmcoreinfo, size)) {
|
if (dfi_mem_read_rc(addr, l.vmcoreinfo, size)) {
|
||||||
zg_free(l.vmcoreinfo);
|
zg_free(l.vmcoreinfo);
|
||||||
@@ -109,6 +122,7 @@ void dfi_vmcoreinfo_init(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
l.vmcoreinfo[size] = 0;
|
l.vmcoreinfo[size] = 0;
|
||||||
|
util_log_print(UTIL_LOG_INFO, "DFI found valid vmcoreinfo\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
+10
-2
@@ -15,6 +15,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
#include "lib/util_log.h"
|
||||||
#include "zgetdump.h"
|
#include "zgetdump.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -46,7 +47,10 @@ static char help_text[] =
|
|||||||
"-s, --select Select system data SYS (\"kdump\", \"prod\", or \"all\")\n"
|
"-s, --select Select system data SYS (\"kdump\", \"prod\", or \"all\")\n"
|
||||||
"-d, --device Print DUMPDEV (dump device) information\n"
|
"-d, --device Print DUMPDEV (dump device) information\n"
|
||||||
"-v, --version Print version information, then exit\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";
|
"-h, --help Print this help, then exit\n";
|
||||||
|
|
||||||
static const char copyright_str[] = "Copyright IBM Corp. 2001, 2018";
|
static const char copyright_str[] = "Copyright IBM Corp. 2001, 2018";
|
||||||
@@ -71,6 +75,9 @@ static void init_defaults(void)
|
|||||||
g.opts.fmt = "s390";
|
g.opts.fmt = "s390";
|
||||||
#endif
|
#endif
|
||||||
dfo_set(g.opts.fmt);
|
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':
|
case 'v':
|
||||||
print_version_exit();
|
print_version_exit();
|
||||||
case 'V':
|
case 'V':
|
||||||
g.opts.verbose_specified = 1;
|
g.opts.verbose++;
|
||||||
|
util_log_set_level(g.opts.verbose);
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
action_set(ZG_ACTION_DUMP_INFO);
|
action_set(ZG_ACTION_DUMP_INFO);
|
||||||
|
|||||||
+1
-1
@@ -49,7 +49,7 @@ static void sig_exit(int sig)
|
|||||||
*/
|
*/
|
||||||
static void sig_handler_init(void)
|
static void sig_handler_init(void)
|
||||||
{
|
{
|
||||||
struct sigaction sigact;
|
struct sigaction sigact = { 0 };
|
||||||
|
|
||||||
/* Ignore signals SIGUSR1 and SIGUSR2 */
|
/* Ignore signals SIGUSR1 and SIGUSR2 */
|
||||||
if (sigemptyset(&sigact.sa_mask) < 0)
|
if (sigemptyset(&sigact.sa_mask) < 0)
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ struct options {
|
|||||||
int argc_fuse;
|
int argc_fuse;
|
||||||
const char *select;
|
const char *select;
|
||||||
int select_specified;
|
int select_specified;
|
||||||
int verbose_specified;
|
int verbose;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const char *OPTS_SELECT_KDUMP;
|
extern const char *OPTS_SELECT_KDUMP;
|
||||||
|
|||||||
@@ -130,6 +130,37 @@ int strncmp(const char *s1, const char *s2, unsigned long count)
|
|||||||
return 0;
|
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)
|
static int skip_atoi(const char **c)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ char *strcat(char *, const char *);
|
|||||||
int strncmp(const char *, const char *, unsigned long);
|
int strncmp(const char *, const char *, unsigned long);
|
||||||
int strlen(const char *);
|
int strlen(const char *);
|
||||||
char *strcpy(char *, 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);
|
unsigned long get_zeroed_page(void);
|
||||||
void free_page(unsigned long);
|
void free_page(unsigned long);
|
||||||
void initialize(void);
|
void initialize(void);
|
||||||
|
|||||||
+204
-6
@@ -131,6 +131,205 @@ secure_boot_enabled()
|
|||||||
return rc;
|
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)
|
void start(void)
|
||||||
{
|
{
|
||||||
unsigned int subchannel_id;
|
unsigned int subchannel_id;
|
||||||
@@ -171,6 +370,9 @@ void start(void)
|
|||||||
/* terminate \0 */
|
/* terminate \0 */
|
||||||
cmdline[COMMAND_LINE_SIZE - 1] = 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 */
|
/* convert extra parameter to ascii */
|
||||||
if (!_stage3_parms.extra_parm || !*cextra)
|
if (!_stage3_parms.extra_parm || !*cextra)
|
||||||
@@ -187,11 +389,6 @@ void start(void)
|
|||||||
cextra++;
|
cextra++;
|
||||||
cextra_len--;
|
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,
|
* if extra parm string starts with '=' replace original string,
|
||||||
* else append
|
* else append
|
||||||
@@ -214,8 +411,9 @@ void start(void)
|
|||||||
/* terminate 0 */
|
/* terminate 0 */
|
||||||
cmdline[cmdline_len + cextra_len] = 0;
|
cmdline[cmdline_len + cextra_len] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
noextra:
|
noextra:
|
||||||
|
handle_environment(cmdline_len + cextra_len);
|
||||||
|
|
||||||
/* copy initrd start address and size intop new kernle space */
|
/* copy initrd start address and size intop new kernle space */
|
||||||
*(unsigned long long *)INITRD_START = _stage3_parms.initrd_addr;
|
*(unsigned long long *)INITRD_START = _stage3_parms.initrd_addr;
|
||||||
*(unsigned long long *)INITRD_SIZE = _stage3_parms.initrd_len;
|
*(unsigned long long *)INITRD_SIZE = _stage3_parms.initrd_len;
|
||||||
|
|||||||
+3
-1
@@ -36,8 +36,10 @@ struct stage3_parms {
|
|||||||
unsigned long long flags; /* flags (e.g. STAGE3_FLAG_KDUMP) */
|
unsigned long long flags; /* flags (e.g. STAGE3_FLAG_KDUMP) */
|
||||||
unsigned long long image_len; /* length of kernel */
|
unsigned long long image_len; /* length of kernel */
|
||||||
unsigned long long image_addr; /* target address 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 struct stage3_parms _stage3_parms;
|
||||||
extern void kdump_stage3();
|
extern void kdump_stage3();
|
||||||
|
|||||||
+2
-1
@@ -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,
|
int boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
|
||||||
address_t initrd_addr, size_t initrd_len,
|
address_t initrd_addr, size_t initrd_len,
|
||||||
address_t entry, int extra_parm, uint64_t flags,
|
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,
|
int boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr,
|
||||||
address_t initrd_addr, address_t image_addr);
|
address_t initrd_addr, address_t image_addr);
|
||||||
int boot_get_tape_dump(void** data, size_t* size, uint64_t mem);
|
int boot_get_tape_dump(void** data, size_t* size, uint64_t mem);
|
||||||
|
|||||||
@@ -17,10 +17,20 @@
|
|||||||
#include "disk.h"
|
#include "disk.h"
|
||||||
#include "job.h"
|
#include "job.h"
|
||||||
#include "zipl.h"
|
#include "zipl.h"
|
||||||
|
#include "stddef.h"
|
||||||
|
|
||||||
|
#define BOOTMAP_HEADER_VERSION 1
|
||||||
|
|
||||||
#define SIGNATURE_MAGIC "~Module signature appended~\n"
|
#define SIGNATURE_MAGIC "~Module signature appended~\n"
|
||||||
#define PKCS7_FORMAT 0x01
|
#define PKCS7_FORMAT 0x01
|
||||||
|
|
||||||
|
struct bootmap_header {
|
||||||
|
char header_text[48];
|
||||||
|
u64 version;
|
||||||
|
u64 envblk_offset;
|
||||||
|
char reserved[448];
|
||||||
|
};
|
||||||
|
|
||||||
struct signature_header {
|
struct signature_header {
|
||||||
uint8_t format;
|
uint8_t format;
|
||||||
uint8_t reserved[3];
|
uint8_t reserved[3];
|
||||||
@@ -50,6 +60,9 @@ struct file_signature {
|
|||||||
|
|
||||||
#define PKEY_ID_PKCS7 0x02
|
#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,
|
int bootmap_create(struct job_data* job, disk_blockptr_t* program_table,
|
||||||
disk_blockptr_t *scsi_dump_sb_blockptr,
|
disk_blockptr_t *scsi_dump_sb_blockptr,
|
||||||
disk_blockptr_t** stage2_list, blocknum_t* stage2_count,
|
disk_blockptr_t** stage2_list, blocknum_t* stage2_count,
|
||||||
|
|||||||
+13
-1
@@ -90,6 +90,11 @@ struct disk_info {
|
|||||||
definition_t targetbase;
|
definition_t targetbase;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct file_range {
|
||||||
|
off_t offset;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
struct job_target_data;
|
struct job_target_data;
|
||||||
|
|
||||||
int disk_get_info(const char* device, struct job_target_data* target,
|
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,
|
blocknum_t disk_write_block_buffer(int fd, int fd_is_basedisk,
|
||||||
const void* buffer, size_t bytecount,
|
const void* buffer, size_t bytecount,
|
||||||
disk_blockptr_t** blocklist,
|
disk_blockptr_t** blocklist,
|
||||||
struct disk_info* info);
|
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_devt(dev_t d);
|
||||||
void disk_print_info(struct disk_info* info);
|
void disk_print_info(struct disk_info* info);
|
||||||
int disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info);
|
int disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info);
|
||||||
blocknum_t disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
|
blocknum_t disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
|
||||||
struct disk_info* info);
|
struct disk_info* info);
|
||||||
blocknum_t disk_get_blocklist_from_file(const char* filename,
|
blocknum_t disk_get_blocklist_from_file(const char* filename,
|
||||||
|
struct file_range *reg,
|
||||||
disk_blockptr_t** blocklist,
|
disk_blockptr_t** blocklist,
|
||||||
struct disk_info* pinfo);
|
struct disk_info* pinfo);
|
||||||
int disk_check_subchannel_set(int devno, dev_t device, char* dev_name);
|
int disk_check_subchannel_set(int devno, dev_t device, char* dev_name);
|
||||||
void disk_print_geo(struct disk_info *data);
|
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 */
|
#endif /* not DISK_H */
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -46,9 +46,15 @@ struct job_ipl_data {
|
|||||||
address_t image_addr;
|
address_t image_addr;
|
||||||
address_t parm_addr;
|
address_t parm_addr;
|
||||||
address_t ramdisk_addr;
|
address_t ramdisk_addr;
|
||||||
|
address_t envblk_addr;
|
||||||
int is_kdump;
|
int is_kdump;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct job_envblk_data {
|
||||||
|
int size;
|
||||||
|
char *buf;
|
||||||
|
};
|
||||||
|
|
||||||
struct job_segment_data {
|
struct job_segment_data {
|
||||||
char* segment;
|
char* segment;
|
||||||
address_t segment_addr;
|
address_t segment_addr;
|
||||||
@@ -108,6 +114,7 @@ struct job_menu_data {
|
|||||||
struct job_data {
|
struct job_data {
|
||||||
enum job_id id;
|
enum job_id id;
|
||||||
struct job_target_data target;
|
struct job_target_data target;
|
||||||
|
struct job_envblk_data envblk;
|
||||||
char* name;
|
char* name;
|
||||||
union {
|
union {
|
||||||
struct job_ipl_data ipl;
|
struct job_ipl_data ipl;
|
||||||
|
|||||||
+2
-1
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
#include "lib/util_base.h"
|
||||||
#include "boot/loaders_layout.h"
|
#include "boot/loaders_layout.h"
|
||||||
|
|
||||||
#define ZIPL_MAGIC "zIPL"
|
#define ZIPL_MAGIC "zIPL"
|
||||||
@@ -35,7 +36,7 @@
|
|||||||
#define ZIPL_DEFAULT_CONF TOOLS_SYSCONFDIR "/zipl.conf"
|
#define ZIPL_DEFAULT_CONF TOOLS_SYSCONFDIR "/zipl.conf"
|
||||||
#define ZIPL_MINIMAL_CONF TOOLS_LIBDIR "/zipl.conf"
|
#define ZIPL_MINIMAL_CONF TOOLS_LIBDIR "/zipl.conf"
|
||||||
#define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries"
|
#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 ZIPL_SIPL_PATH "/sys/firmware/ipl/has_secure"
|
||||||
|
|
||||||
#define MENU_DEFAULT_PROMPT 0
|
#define MENU_DEFAULT_PROMPT 0
|
||||||
|
|||||||
@@ -9,10 +9,14 @@ install:
|
|||||||
sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \
|
sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \
|
||||||
-e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.8.in \
|
-e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.8.in \
|
||||||
> $(DESTDIR)$(MANDIR)/man8/zipl.8
|
> $(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)@' \
|
sed -e 's@%SYSCONFDIR%@$(SYSCONFDIR)@' \
|
||||||
-e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.conf.5.in \
|
-e 's@%TOOLS_LIBDIR%@$(TOOLS_LIBDIR)@' zipl.conf.5.in \
|
||||||
> $(DESTDIR)$(MANDIR)/man5/zipl.conf.5
|
> $(DESTDIR)$(MANDIR)/man5/zipl.conf.5
|
||||||
chmod 644 $(DESTDIR)$(MANDIR)/man8/zipl.8 \
|
chmod 644 $(DESTDIR)$(MANDIR)/man8/zipl.8 \
|
||||||
|
$(DESTDIR)$(MANDIR)/man8/zipl-editenv.8 \
|
||||||
$(DESTDIR)$(MANDIR)/man5/zipl.conf.5
|
$(DESTDIR)$(MANDIR)/man5/zipl.conf.5
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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
|
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.
|
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
|
.TP
|
||||||
.BR "\-\-targetbase=<BASE DEVICE>"
|
.BR "\-\-targetbase=<BASE DEVICE>"
|
||||||
Install the actual boot loader on the device node specified by 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
|
It is not possible to specify both this parameter and the name of a menu
|
||||||
section on the command line at the same time.
|
section on the command line at the same time.
|
||||||
|
|
||||||
|
The command line can contain zIPL environment variables (see
|
||||||
|
.BR zipl-editenv(8)).
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BR "\-k auto" " or " "\-\-kdump=auto"
|
.BR "\-k auto" " or " "\-\-kdump=auto"
|
||||||
Install a kdump kernel that can be used as a stand-alone dump tool. You
|
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
|
.SH SEE ALSO
|
||||||
.BR zipl.conf (5),
|
.BR zipl.conf (5),
|
||||||
|
.BR zipl-editenv (8),
|
||||||
.BR kexec (8)
|
.BR kexec (8)
|
||||||
|
|||||||
@@ -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
|
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
|
kernel at boot time. Note that the parameter line may be enclosed in quotes
|
||||||
(" or ') to preserve leading and trailing spaces.
|
(" or ') to preserve leading and trailing spaces.
|
||||||
|
The parameter line can contain zIPL environment variables (see
|
||||||
|
.B zipl-editenv(8)).
|
||||||
.PP
|
.PP
|
||||||
|
|
||||||
.B parmfile
|
.B parmfile
|
||||||
@@ -710,4 +712,5 @@ for example via the "/dev/disk/by-path/" device nodes.
|
|||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.BR zipl (8),
|
.BR zipl (8),
|
||||||
|
.BR zipl-editenv (8),
|
||||||
.BR zgetdump (8)
|
.BR zgetdump (8)
|
||||||
|
|||||||
+8
-4
@@ -9,14 +9,14 @@ ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS)
|
|||||||
|
|
||||||
libs = $(rootdir)/libutil/libutil.a
|
libs = $(rootdir)/libutil/libutil.a
|
||||||
|
|
||||||
objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \
|
objects = misc.o error.o scan.o job.o boot.o bootmap.o fs-map.o disk.o \
|
||||||
install.o zipl.o $(rootdir)/zipl/boot/data.o
|
bootmap_header.o envblk.o install.o zipl.o $(rootdir)/zipl/boot/data.o
|
||||||
|
|
||||||
zipl_helpers = $(basename $(wildcard zipl_helper.*.c))
|
zipl_helpers = $(basename $(wildcard zipl_helper.*.c))
|
||||||
chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers))
|
chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers))
|
||||||
zipl_stage3 = ../boot/stage3.bin
|
zipl_stage3 = ../boot/stage3.bin
|
||||||
|
|
||||||
all: zipl $(chreipl_helpers) $(zipl_stage3)
|
all: zipl zipl-editenv $(chreipl_helpers) $(zipl_stage3)
|
||||||
|
|
||||||
zipl: $(objects) $(libs)
|
zipl: $(objects) $(libs)
|
||||||
|
|
||||||
@@ -24,19 +24,23 @@ zipl_helper.device-mapper: $(rootdir)/libdasd/libdasd.a \
|
|||||||
$(rootdir)/libvtoc/libvtoc.a \
|
$(rootdir)/libvtoc/libvtoc.a \
|
||||||
$(libs) zipl_helper.device-mapper.o
|
$(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.%
|
chreipl_helper.%: zipl_helper.%
|
||||||
ln -f -s $< $@
|
ln -f -s $< $@
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
|
||||||
$(INSTALL) -c zipl $(DESTDIR)$(BINDIR)
|
$(INSTALL) -c zipl $(DESTDIR)$(BINDIR)
|
||||||
|
$(INSTALL) -c zipl-editenv $(DESTDIR)$(BINDIR)
|
||||||
$(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \
|
$(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \
|
||||||
$(DESTDIR)$(TOOLS_LIBDIR)
|
$(DESTDIR)$(TOOLS_LIBDIR)
|
||||||
$(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR)
|
$(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR)
|
||||||
$(CP) --no-dereference $(zipl_stage3) $(DESTDIR)$(TOOLS_LIBDIR)
|
$(CP) --no-dereference $(zipl_stage3) $(DESTDIR)$(TOOLS_LIBDIR)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl
|
rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl zipl-editenv
|
||||||
|
|
||||||
.PHONY: all install clean
|
.PHONY: all install clean
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -82,7 +82,8 @@ int
|
|||||||
boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
|
boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
|
||||||
address_t initrd_addr, size_t initrd_len,
|
address_t initrd_addr, size_t initrd_len,
|
||||||
address_t entry, int extra_parm, uint64_t flags,
|
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;
|
struct stage3_parms params;
|
||||||
void* data;
|
void* data;
|
||||||
@@ -106,6 +107,8 @@ boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
|
|||||||
params.flags = flags;
|
params.flags = flags;
|
||||||
params.image_len = (uint64_t) image_len;
|
params.image_len = (uint64_t) image_len;
|
||||||
params.image_addr = (uint64_t) image_addr;
|
params.image_addr = (uint64_t) image_addr;
|
||||||
|
params.envblk_addr = (uint64_t) envblk_addr;
|
||||||
|
params.envblk_len = (uint64_t) envblk_len;
|
||||||
/* Initialize buffer */
|
/* Initialize buffer */
|
||||||
memcpy(data, ¶ms, sizeof(params));
|
memcpy(data, ¶ms, sizeof(params));
|
||||||
*buffer = data;
|
*buffer = data;
|
||||||
|
|||||||
+122
-33
@@ -17,6 +17,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
#include "lib/util_part.h"
|
#include "lib/util_part.h"
|
||||||
@@ -26,19 +27,15 @@
|
|||||||
|
|
||||||
#include "boot.h"
|
#include "boot.h"
|
||||||
#include "bootmap.h"
|
#include "bootmap.h"
|
||||||
|
#include "envblk.h"
|
||||||
#include "disk.h"
|
#include "disk.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "install.h"
|
#include "install.h"
|
||||||
#include "misc.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. */
|
/* Pointer to dedicated empty block in bootmap. */
|
||||||
disk_blockptr_t empty_block;
|
disk_blockptr_t empty_block;
|
||||||
|
|
||||||
|
|
||||||
/* Get size of a bootmap block pointer for disk with given INFO. */
|
/* Get size of a bootmap block pointer for disk with given INFO. */
|
||||||
static int
|
static int
|
||||||
get_blockptr_size(struct disk_info* info)
|
get_blockptr_size(struct disk_info* info)
|
||||||
@@ -291,10 +288,11 @@ struct component_loc {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
add_component_file(int fd, const char* filename, address_t load_address,
|
add_component_file_range(int fd, const char *filename, struct file_range *reg,
|
||||||
size_t trailer, void *component, int add_files,
|
address_t load_address,
|
||||||
struct disk_info* info, struct job_target_data* target,
|
size_t trailer, void *component, int add_files,
|
||||||
struct component_loc *location)
|
struct disk_info *info, struct job_target_data *target,
|
||||||
|
struct component_loc *location)
|
||||||
{
|
{
|
||||||
struct disk_info* file_info;
|
struct disk_info* file_info;
|
||||||
struct component_loc loc;
|
struct component_loc loc;
|
||||||
@@ -306,6 +304,7 @@ add_component_file(int fd, const char* filename, address_t load_address,
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (add_files) {
|
if (add_files) {
|
||||||
|
assert(reg == NULL); /* not implemented */
|
||||||
/* Read file to buffer */
|
/* Read file to buffer */
|
||||||
rc = misc_read_file(filename, &buffer, &size, 0);
|
rc = misc_read_file(filename, &buffer, &size, 0);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
@@ -332,8 +331,8 @@ add_component_file(int fd, const char* filename, address_t load_address,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* Get block list from existing file */
|
/* Get block list from existing file */
|
||||||
count = disk_get_blocklist_from_file(filename, &list,
|
count = disk_get_blocklist_from_file(filename, reg,
|
||||||
file_info);
|
&list, file_info);
|
||||||
disk_free_info(file_info);
|
disk_free_info(file_info);
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -358,9 +357,22 @@ add_component_file(int fd, const char* filename, address_t load_address,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
add_component_buffer(int fd, void* buffer, size_t size, component_data data,
|
add_component_file(int fd, const char *filename, address_t load_address,
|
||||||
void* component, struct disk_info* info,
|
size_t trailer, void *component, int add_files,
|
||||||
struct component_loc *location, int type)
|
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;
|
struct component_loc loc;
|
||||||
disk_blockptr_t segment;
|
disk_blockptr_t segment;
|
||||||
@@ -369,7 +381,8 @@ add_component_buffer(int fd, void* buffer, size_t size, component_data data,
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Write buffer */
|
/* 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) {
|
if (count == 0) {
|
||||||
error_text("Could not write to bootmap file");
|
error_text("Could not write to bootmap file");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -396,6 +409,15 @@ add_component_buffer(int fd, void* buffer, size_t size, component_data data,
|
|||||||
return rc;
|
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
|
static int
|
||||||
add_dummy_buffer(int fd, size_t size, address_t addr, void *component,
|
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
|
static int
|
||||||
extract_signature(char *filename, void **ret_signature,
|
extract_signature(const char *filename, void **ret_signature,
|
||||||
struct signature_header *sig_head)
|
struct signature_header *sig_head)
|
||||||
{
|
{
|
||||||
struct file_signature *file_sig;
|
struct file_signature *file_sig;
|
||||||
@@ -496,8 +518,9 @@ check_remaining_filesize(size_t filesize, size_t signature_size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int add_ipl_program(int fd, char *filename,
|
||||||
add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
|
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,
|
int verbose, int add_files, component_header_type type,
|
||||||
struct disk_info* info, struct job_target_data* target,
|
struct disk_info* info, struct job_target_data* target,
|
||||||
int is_secure)
|
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;
|
size_t ramdisk_size, image_size;
|
||||||
bool secure_boot_supported;
|
bool secure_boot_supported;
|
||||||
size_t stage3_params_size;
|
size_t stage3_params_size;
|
||||||
const char *comp_name[10];
|
const char *comp_name[11];
|
||||||
size_t signature_size;
|
size_t signature_size;
|
||||||
int offset;
|
int offset;
|
||||||
uint64_t flags = 0;
|
uint64_t flags = 0;
|
||||||
void *stage3_params;
|
void *stage3_params;
|
||||||
struct stat stats;
|
struct stat stats;
|
||||||
|
off_t envblk_off;
|
||||||
void *signature;
|
void *signature;
|
||||||
int comp_nr = 0;
|
int comp_nr = 0;
|
||||||
void *table;
|
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 :
|
ipl->is_kdump ? IMAGE_ENTRY_KDUMP :
|
||||||
IMAGE_ENTRY,
|
IMAGE_ENTRY,
|
||||||
(info->type == disk_type_scsi) ? 0 : 1,
|
(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) {
|
if (rc) {
|
||||||
free(table);
|
free(table);
|
||||||
return rc;
|
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_name[comp_nr] = "parmline";
|
||||||
comp_nr++;
|
comp_nr++;
|
||||||
}
|
}
|
||||||
|
/* add ramdisk */
|
||||||
/* finally add ramdisk */
|
|
||||||
if (ipl->ramdisk != NULL) {
|
if (ipl->ramdisk != NULL) {
|
||||||
signature_size = extract_signature(ipl->ramdisk, &signature,
|
signature_size = extract_signature(ipl->ramdisk, &signature,
|
||||||
&sig_head);
|
&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_name[comp_nr] = "initial ramdisk";
|
||||||
comp_nr++;
|
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, ®,
|
||||||
|
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)
|
if (verbose)
|
||||||
print_components(comp_name, comp_loc, comp_nr);
|
print_components(comp_name, comp_loc, comp_nr);
|
||||||
/* Terminate component table */
|
/* 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,
|
rc = add_component_file(fd, segment->segment, segment->segment_addr, 0,
|
||||||
VOID_ADD(table, offset), add_files, info,
|
VOID_ADD(table, offset), add_files, info,
|
||||||
target, &comp_loc[0]);
|
target, &comp_loc[0]);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
error_text("Could not add segment file '%s'",
|
error_text("Could not add segment file '%s'",
|
||||||
segment->segment);
|
segment->segment);
|
||||||
free(table);
|
free(table);
|
||||||
@@ -936,7 +1019,7 @@ add_dump_program(int fd, struct job_dump_data* dump,
|
|||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
ipl.parm_addr = dump->parm_addr;
|
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);
|
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
|
/* Build a program table from job data and set pointer to program table
|
||||||
* block upon success. */
|
* block upon success. */
|
||||||
static int
|
static int
|
||||||
build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
|
build_program_table(int fd, char *filename, struct job_data *job,
|
||||||
struct disk_info* info)
|
disk_blockptr_t *pointer, struct disk_info *info)
|
||||||
{
|
{
|
||||||
disk_blockptr_t* table;
|
disk_blockptr_t* table;
|
||||||
int entries, component_header;
|
int entries, component_header;
|
||||||
@@ -959,6 +1042,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
|
|||||||
entries);
|
entries);
|
||||||
if (table == NULL)
|
if (table == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memset((void *) table, 0, sizeof(disk_blockptr_t) * entries);
|
memset((void *) table, 0, sizeof(disk_blockptr_t) * entries);
|
||||||
/* Add programs */
|
/* Add programs */
|
||||||
switch (job->id) {
|
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;
|
component_header = component_header_dump;
|
||||||
else
|
else
|
||||||
component_header = component_header_ipl;
|
component_header = component_header_ipl;
|
||||||
rc = add_ipl_program(fd, &job->data.ipl, &table[0],
|
rc = add_ipl_program(fd, filename,
|
||||||
verbose || job->command_line,
|
true, &job->envblk, &job->data.ipl,
|
||||||
|
&table[0], verbose || job->command_line,
|
||||||
job->add_files, component_header,
|
job->add_files, component_header,
|
||||||
info, &job->target, job->is_secure);
|
info, &job->target, job->is_secure);
|
||||||
break;
|
break;
|
||||||
@@ -1026,7 +1111,8 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
|
|||||||
else
|
else
|
||||||
is_secure =
|
is_secure =
|
||||||
job->data.menu.entry[i].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,
|
&job->data.menu.entry[i].data.ipl,
|
||||||
&table[job->data.menu.entry[i].pos],
|
&table[job->data.menu.entry[i].pos],
|
||||||
verbose || job->command_line,
|
verbose || job->command_line,
|
||||||
@@ -1060,6 +1146,9 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
|
|||||||
rc = -1;
|
rc = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (job->envblk.buf && verbose)
|
||||||
|
envblk_print(job->envblk.buf, job->envblk.size);
|
||||||
|
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
/* Add program table block */
|
/* Add program table block */
|
||||||
rc = add_program_table(fd, table, entries, pointer, info);
|
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;
|
scsi_sb.dump_size = unused_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write bootmap header */
|
/* Initialize bootmap header */
|
||||||
if (misc_write(fd, header_text, sizeof(header_text))) {
|
if (bootmap_header_init(fd)) {
|
||||||
error_text("Could not write to file '%s'", filename);
|
error_text("Could not init bootmap header at '%s'", filename);
|
||||||
goto out_misc_free_temp_dev;
|
goto out_misc_free_temp_dev;
|
||||||
}
|
}
|
||||||
/* Write empty block to be read in place of holes in files */
|
/* 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;
|
goto out_misc_free_temp_dev;
|
||||||
}
|
}
|
||||||
/* Build program table */
|
/* 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;
|
goto out_misc_free_temp_dev;
|
||||||
if (job->id == job_dump_partition) {
|
if (job->id == job_dump_partition) {
|
||||||
scsi_sb.magic = SCSI_DUMP_SB_MAGIC;
|
scsi_sb.magic = SCSI_DUMP_SB_MAGIC;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
+97
-124
@@ -23,10 +23,10 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/fiemap.h>
|
#include <linux/fiemap.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "lib/util_proc.h"
|
#include "lib/util_proc.h"
|
||||||
#include "lib/util_sys.h"
|
#include "lib/util_sys.h"
|
||||||
|
|
||||||
#include "disk.h"
|
#include "disk.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "install.h"
|
#include "install.h"
|
||||||
@@ -198,7 +198,7 @@ disk_get_info(const char* device, struct job_target_data* target,
|
|||||||
int fd;
|
int fd;
|
||||||
long devsize;
|
long devsize;
|
||||||
FILE *fh;
|
FILE *fh;
|
||||||
char *script_pre = TOOLS_LIBDIR "/zipl_helper.";
|
const char *script_pre = util_libdir_path("/zipl_helper.");
|
||||||
char *script_file = NULL;
|
char *script_file = NULL;
|
||||||
char *ppn_cmd = NULL;
|
char *ppn_cmd = NULL;
|
||||||
char buffer[80];
|
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
|
/* Retrieve the physical blocknumber (block on disk) of the specified logical
|
||||||
* block (block in file). FD provides the file descriptor, LOGICAL is the
|
* block (block in file). FD provides the file descriptor, LOGICAL is the
|
||||||
* logical block number. Upon success, return 0 and store the physical
|
* 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,
|
disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
|
||||||
blocknum_t* physical, struct disk_info* info)
|
blocknum_t* physical, struct disk_info* info)
|
||||||
{
|
{
|
||||||
struct statfs buf;
|
|
||||||
blocknum_t phy_per_fs;
|
blocknum_t phy_per_fs;
|
||||||
blocknum_t mapped;
|
blocknum_t mapped;
|
||||||
int block;
|
|
||||||
int subblock;
|
int subblock;
|
||||||
int fiemap_size;
|
|
||||||
int map_offset;
|
|
||||||
struct fiemap *fiemap;
|
|
||||||
|
|
||||||
/* No file system: partition or raw disk */
|
/* No file system: partition or raw disk */
|
||||||
if (info->fs_block_size == -1) {
|
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;
|
*physical = logical + info->geo.start;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
/* Get file system type */
|
* Get mapping in file system blocks
|
||||||
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 */
|
|
||||||
phy_per_fs = info->fs_block_size / info->phy_block_size;
|
phy_per_fs = info->fs_block_size / info->phy_block_size;
|
||||||
subblock = logical % phy_per_fs;
|
subblock = logical % phy_per_fs;
|
||||||
|
|
||||||
/* First try FIEMAP, more complicated to set up */
|
if (fs_map(fd, logical * info->phy_block_size,
|
||||||
fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent);
|
&mapped, info->fs_block_size) != 0)
|
||||||
|
|
||||||
fiemap = misc_malloc(fiemap_size);
|
|
||||||
if (!fiemap)
|
|
||||||
return -1;
|
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) {
|
if (mapped == 0) {
|
||||||
/* This is a hole in the file */
|
/* This is a hole in the file */
|
||||||
*physical = 0;
|
*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
|
* 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
|
* the file identified by file descriptor FD at the current position in that
|
||||||
* aligned on a block size boundary and that at most INFO->PHY_BLOCK_SIZE
|
* file aligned on ALIGN block size boundary and make sure that at most
|
||||||
* bytes are written. INFO provides information about the disk layout. Upon
|
* INFO->PHY_BLOCK_SIZE bytes are written. INFO provides information about
|
||||||
* success, return 0 and store the pointer to the resulting disk block to BLOCK
|
* the disk layout. Upon success, store the pointer to the resulting disk
|
||||||
* (if BLOCK is not NULL). Return non-zero otherwise. */
|
* 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
|
static int
|
||||||
disk_write_block_aligned_base(int fd, int is_base_disk, const void* data,
|
disk_write_block_aligned_base(int fd, int is_base_disk, const void* data,
|
||||||
size_t bytecount, disk_blockptr_t* block,
|
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 current_block;
|
||||||
blocknum_t blocknum;
|
blocknum_t blocknum;
|
||||||
off_t current_pos;
|
off_t current_pos;
|
||||||
int align;
|
|
||||||
|
if (align == 0)
|
||||||
|
align = info->phy_block_size;
|
||||||
|
|
||||||
current_pos = lseek(fd, 0, SEEK_CUR);
|
current_pos = lseek(fd, 0, SEEK_CUR);
|
||||||
if (current_pos == -1) {
|
if (current_pos == -1) {
|
||||||
@@ -751,18 +687,18 @@ disk_write_block_aligned_base(int fd, int is_base_disk, const void* data,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* Ensure block alignment of current file pos */
|
/* Ensure block alignment of current file pos */
|
||||||
align = info->phy_block_size;
|
|
||||||
if (current_pos % align != 0) {
|
if (current_pos % align != 0) {
|
||||||
current_pos = lseek(fd, align - current_pos % align, SEEK_CUR);
|
current_pos = lseek(fd, align - current_pos % align, SEEK_CUR);
|
||||||
if (current_pos == -1) {
|
if (current_pos == -1) {
|
||||||
error_text(strerror(errno));
|
error_text(strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
current_block = current_pos / align;
|
current_block = current_pos / info->phy_block_size;
|
||||||
/* Ensure maximum size */
|
/* Ensure maximum size */
|
||||||
if (bytecount > (size_t) align)
|
if (bytecount > (size_t)info->phy_block_size)
|
||||||
bytecount = align;
|
bytecount = info->phy_block_size;
|
||||||
/* Write data block */
|
/* Write data block */
|
||||||
if (misc_write(fd, data, bytecount))
|
if (misc_write(fd, data, bytecount))
|
||||||
return -1;
|
return -1;
|
||||||
@@ -773,33 +709,38 @@ disk_write_block_aligned_base(int fd, int is_base_disk, const void* data,
|
|||||||
return -1;
|
return -1;
|
||||||
disk_blockptr_from_blocknum(block, blocknum, info);
|
disk_blockptr_from_blocknum(block, blocknum, info);
|
||||||
}
|
}
|
||||||
|
if (offset)
|
||||||
|
*offset = current_pos;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int disk_write_block_aligned(int fd, const void *data, size_t bytecount,
|
||||||
disk_write_block_aligned(int fd, const void* data, size_t bytecount,
|
disk_blockptr_t *block, struct disk_info *info)
|
||||||
disk_blockptr_t* block, struct disk_info* info)
|
|
||||||
{
|
{
|
||||||
return disk_write_block_aligned_base(fd, 0, data, bytecount, block,
|
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
|
* Write BYTECOUNT bytes from memory at location BUFFER to the file identified
|
||||||
* that make up the respective part of the file. Upon success return the number
|
* by file descriptor FD at the current position in that file aligned on ALIGN
|
||||||
* of blocks and set BLOCKLIST to point to the uncompressed list. Return zero
|
* block size boundary and return the list of pointers to the disk blocks that
|
||||||
* otherwise. Note that the data is written to a file position which is aligned
|
* make up the respective part of the file. Upon success return the number of
|
||||||
* on a block size boundary. */
|
* 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
|
blocknum_t
|
||||||
disk_write_block_buffer(int fd, int fd_is_basedisk, const void* buffer,
|
disk_write_block_buffer_align(int fd, int fd_is_basedisk, const void *buffer,
|
||||||
size_t bytecount, disk_blockptr_t** blocklist,
|
size_t bytecount, disk_blockptr_t **blocklist,
|
||||||
struct disk_info* info)
|
struct disk_info *info, int align, off_t *offset)
|
||||||
{
|
{
|
||||||
disk_blockptr_t* list;
|
disk_blockptr_t* list;
|
||||||
blocknum_t count;
|
blocknum_t count;
|
||||||
blocknum_t i;
|
blocknum_t i;
|
||||||
size_t written;
|
size_t written;
|
||||||
size_t chunk_size;
|
size_t chunk_size;
|
||||||
|
off_t pos;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
count = (bytecount + info->phy_block_size - 1) / info->phy_block_size;
|
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)
|
if (chunk_size > (size_t) info->phy_block_size)
|
||||||
chunk_size = info->phy_block_size;
|
chunk_size = info->phy_block_size;
|
||||||
rc = disk_write_block_aligned_base(fd, fd_is_basedisk,
|
rc = disk_write_block_aligned_base(fd, fd_is_basedisk,
|
||||||
VOID_ADD(buffer, written),
|
VOID_ADD(buffer, written),
|
||||||
chunk_size,
|
chunk_size, &list[i], info,
|
||||||
&list[i], info);
|
i == 0 ? align : info->phy_block_size,
|
||||||
|
&pos);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
free(list);
|
free(list);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (offset != NULL && i == 0)
|
||||||
|
*offset = pos;
|
||||||
}
|
}
|
||||||
*blocklist = list;
|
*blocklist = list;
|
||||||
return count;
|
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. */
|
/* Print device node. */
|
||||||
void
|
void
|
||||||
@@ -1064,19 +1017,26 @@ disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
|
|||||||
return last + 1;
|
return last + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
/* Retrieve a list of pointers to the disk blocks that make up the file
|
* Retrieve a list of pointers to the disk blocks that make up a continuous
|
||||||
* specified by FILENAME. Upon success, return the number of blocks and set
|
* region REG in a file specified by FILENAME. If REG is NULL, then retrieve
|
||||||
* BLOCKLIST to point to the uncompacted list. INFO provides information
|
* a list of pointers for the whole file.
|
||||||
* about the device which contains the file. Return zero otherwise. */
|
* 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
|
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)
|
struct disk_info* info)
|
||||||
{
|
{
|
||||||
disk_blockptr_t* list;
|
disk_blockptr_t* list;
|
||||||
struct stat stats;
|
struct stat stats;
|
||||||
int fd;
|
int fd;
|
||||||
blocknum_t count;
|
off_t off;
|
||||||
|
size_t count;
|
||||||
|
blocknum_t blk_off;
|
||||||
|
blocknum_t blk_count;
|
||||||
blocknum_t i;
|
blocknum_t i;
|
||||||
blocknum_t blocknum;
|
blocknum_t blocknum;
|
||||||
|
|
||||||
@@ -1093,24 +1053,37 @@ disk_get_blocklist_from_file(const char* filename, disk_blockptr_t** blocklist,
|
|||||||
close (fd);
|
close (fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* Get number of blocks */
|
if (reg) {
|
||||||
count = ((blocknum_t) stats.st_size +
|
/*
|
||||||
info->phy_block_size - 1) / info->phy_block_size;
|
* case of not block-aligned offsets is not implemented
|
||||||
if (count == 0) {
|
*/
|
||||||
error_reason("Could not read empty file '%s'", filename);
|
assert(reg->offset % info->phy_block_size == 0);
|
||||||
close(fd);
|
|
||||||
return 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) *
|
list = (disk_blockptr_t *) misc_malloc(sizeof(disk_blockptr_t) *
|
||||||
count);
|
blk_count);
|
||||||
if (list == NULL) {
|
if (list == NULL) {
|
||||||
close(fd);
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
memset((void *) list, 0, sizeof(disk_blockptr_t) * count);
|
memset((void *) list, 0, sizeof(disk_blockptr_t) * blk_count);
|
||||||
/* Build list */
|
/* Build list */
|
||||||
for (i=0; i < count; i++) {
|
for (i = 0; i < blk_count; i++) {
|
||||||
if (disk_get_blocknum(fd, 0, i, &blocknum, info)) {
|
if (disk_get_blocknum(fd, 0, blk_off + i, &blocknum, info)) {
|
||||||
free(list);
|
free(list);
|
||||||
close(fd);
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1119,7 +1092,7 @@ disk_get_blocklist_from_file(const char* filename, disk_blockptr_t** blocklist,
|
|||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
*blocklist = list;
|
*blocklist = list;
|
||||||
return count;
|
return blk_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check whether input device is in subchannel set 0.
|
/* Check whether input device is in subchannel set 0.
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
+90
-14
@@ -14,6 +14,7 @@
|
|||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "scan.h"
|
#include "scan.h"
|
||||||
#include "zipl.h"
|
#include "zipl.h"
|
||||||
|
#include "envblk.h"
|
||||||
|
|
||||||
/* Command line options */
|
/* Command line options */
|
||||||
static struct option options[] = {
|
static struct option options[] = {
|
||||||
@@ -37,6 +39,7 @@ static struct option options[] = {
|
|||||||
{ "targetgeometry", required_argument, NULL, 0xac},
|
{ "targetgeometry", required_argument, NULL, 0xac},
|
||||||
{ "targetblocksize", required_argument, NULL, 0xad},
|
{ "targetblocksize", required_argument, NULL, 0xad},
|
||||||
{ "targetoffset", required_argument, NULL, 0xae},
|
{ "targetoffset", required_argument, NULL, 0xae},
|
||||||
|
{ "environment", required_argument, NULL, 0xaf},
|
||||||
{ "image", required_argument, NULL, 'i'},
|
{ "image", required_argument, NULL, 'i'},
|
||||||
{ "ramdisk", required_argument, NULL, 'r'},
|
{ "ramdisk", required_argument, NULL, 'r'},
|
||||||
{ "parmfile", required_argument, NULL, 'p'},
|
{ "parmfile", required_argument, NULL, 'p'},
|
||||||
@@ -73,6 +76,7 @@ static const char *zipl_conf[] = {
|
|||||||
struct command_line {
|
struct command_line {
|
||||||
char* data[SCAN_KEYWORD_NUM];
|
char* data[SCAN_KEYWORD_NUM];
|
||||||
char* config;
|
char* config;
|
||||||
|
char *envblk_import_hint;
|
||||||
char *blsdir;
|
char *blsdir;
|
||||||
char* menu;
|
char* menu;
|
||||||
char* section;
|
char* section;
|
||||||
@@ -87,7 +91,6 @@ struct command_line {
|
|||||||
enum scan_section_type type;
|
enum scan_section_type type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
store_option(struct command_line* cmdline, enum scan_keyword_id keyword,
|
store_option(struct command_line* cmdline, enum scan_keyword_id keyword,
|
||||||
char* value)
|
char* value)
|
||||||
@@ -211,6 +214,13 @@ get_command_line(int argc, char* argv[], struct command_line* line)
|
|||||||
rc = store_option(&cmdline, scan_keyword_targetoffset,
|
rc = store_option(&cmdline, scan_keyword_targetoffset,
|
||||||
optarg);
|
optarg);
|
||||||
break;
|
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':
|
case 'T':
|
||||||
is_keyword = 1;
|
is_keyword = 1;
|
||||||
rc = store_option(&cmdline, scan_keyword_tape,
|
rc = store_option(&cmdline, scan_keyword_tape,
|
||||||
@@ -351,6 +361,12 @@ free_target_data(struct job_target_data* data)
|
|||||||
free(data->targetbase);
|
free(data->targetbase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_envblk_data(struct job_envblk_data *data)
|
||||||
|
{
|
||||||
|
free(data->buf);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
free_ipl_data(struct job_ipl_data* data)
|
free_ipl_data(struct job_ipl_data* data)
|
||||||
{
|
{
|
||||||
@@ -444,6 +460,7 @@ job_free(struct job_data* job)
|
|||||||
if (job->target.bootmap_dir != NULL)
|
if (job->target.bootmap_dir != NULL)
|
||||||
free(job->target.bootmap_dir);
|
free(job->target.bootmap_dir);
|
||||||
free_target_data(&job->target);
|
free_target_data(&job->target);
|
||||||
|
free_envblk_data(&job->envblk);
|
||||||
if (job->name != NULL)
|
if (job->name != NULL)
|
||||||
free(job->name);
|
free(job->name);
|
||||||
switch (job->id) {
|
switch (job->id) {
|
||||||
@@ -480,7 +497,7 @@ struct component_loc {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static int
|
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)
|
address_t *addrp, size_t size, off_t off, off_t align)
|
||||||
{
|
{
|
||||||
struct stat stats;
|
struct stat stats;
|
||||||
@@ -528,17 +545,16 @@ sort_cl_array(struct component_loc* cl, int elements)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp,
|
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;
|
struct component_loc *cl;
|
||||||
int num;
|
int num;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Get memory for image, parmline, ramdisk */
|
/* Get memory for image, parmline, ramdisk and environment block */
|
||||||
cl = misc_calloc(3, sizeof(struct component_loc));
|
cl = misc_calloc(4, sizeof(struct component_loc));
|
||||||
if (cl == NULL)
|
if (cl == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
/* Fill in component data */
|
/* Fill in component data */
|
||||||
@@ -561,6 +577,12 @@ get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp,
|
|||||||
if (rc)
|
if (rc)
|
||||||
goto error;
|
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;
|
*clp = cl;
|
||||||
*nump = num;
|
*nump = num;
|
||||||
@@ -753,7 +775,8 @@ finalize_component_address_data(struct component_loc *cl, int num,
|
|||||||
|
|
||||||
|
|
||||||
static int
|
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;
|
unsigned long address_limit;
|
||||||
struct component_loc *cl;
|
struct component_loc *cl;
|
||||||
@@ -762,7 +785,7 @@ finalize_ipl_address_data(struct job_ipl_data* ipl, char *name)
|
|||||||
|
|
||||||
address_limit = ipl->is_kdump ?
|
address_limit = ipl->is_kdump ?
|
||||||
MIN(util_arch_hsa_maxsize(), ADDRESS_LIMIT) : ADDRESS_LIMIT;
|
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)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
sort_cl_array(cl, num);
|
sort_cl_array(cl, num);
|
||||||
@@ -828,7 +851,8 @@ out_free:
|
|||||||
|
|
||||||
|
|
||||||
static int
|
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;
|
int rc;
|
||||||
|
|
||||||
@@ -856,7 +880,7 @@ check_job_ipl_data(struct job_ipl_data *ipl, char* name)
|
|||||||
return rc;
|
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
|
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 rc;
|
||||||
int i;
|
int i;
|
||||||
@@ -944,7 +968,8 @@ check_job_menu_data(struct job_menu_data* menu)
|
|||||||
switch (menu->entry[i].id) {
|
switch (menu->entry[i].id) {
|
||||||
case job_ipl:
|
case job_ipl:
|
||||||
rc = check_job_ipl_data(&menu->entry[i].data.ipl,
|
rc = check_job_ipl_data(&menu->entry[i].data.ipl,
|
||||||
menu->entry[i].name);
|
menu->entry[i].name,
|
||||||
|
envblk);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
break;
|
break;
|
||||||
@@ -1110,10 +1135,11 @@ check_job_data(struct job_data* job)
|
|||||||
rc = 0;
|
rc = 0;
|
||||||
break;
|
break;
|
||||||
case job_ipl:
|
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;
|
break;
|
||||||
case job_menu:
|
case job_menu:
|
||||||
rc = check_job_menu_data(&job->data.menu);
|
rc = check_job_menu_data(&job->data.menu, &job->envblk);
|
||||||
break;
|
break;
|
||||||
case job_segment:
|
case job_segment:
|
||||||
rc = check_job_segment_data(&job->data.segment, job->name);
|
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);
|
&job->data.ipl.parm_addr, section);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
/* Fill in environment block */
|
||||||
|
job->data.ipl.envblk_addr = UNSPECIFIED_ADDRESS;
|
||||||
|
|
||||||
/* Fill in name and address of ramdisk file */
|
/* Fill in name and address of ramdisk file */
|
||||||
if (data[(int) scan_keyword_ramdisk] != NULL) {
|
if (data[(int) scan_keyword_ramdisk] != NULL) {
|
||||||
job->data.ipl.ramdisk =
|
job->data.ipl.ramdisk =
|
||||||
@@ -1898,6 +1927,45 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job)
|
|||||||
return rc;
|
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
|
int
|
||||||
job_get(int argc, char* argv[], struct job_data** data)
|
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->data.mvdump.force = cmdline.force;
|
||||||
job->dry_run = cmdline.dry_run;
|
job->dry_run = cmdline.dry_run;
|
||||||
job->is_secure = SECURE_BOOT_UNDEFINED;
|
job->is_secure = SECURE_BOOT_UNDEFINED;
|
||||||
|
if (job->verbose)
|
||||||
|
printf("Looking for components in '%s'\n", util_libdir());
|
||||||
|
|
||||||
/* Get job data from user input */
|
/* Get job data from user input */
|
||||||
if (cmdline.help) {
|
if (cmdline.help) {
|
||||||
job->command_line = 1;
|
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)
|
else if (job->id != job_menu && job->is_secure == SECURE_BOOT_UNDEFINED)
|
||||||
job->is_secure = SECURE_BOOT_AUTO;
|
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 */
|
/* Check job data for validity */
|
||||||
rc = check_job_data(job);
|
rc = check_job_data(job);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
+104
-34
@@ -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
|
* 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 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).
|
* 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,
|
static int open_keyslot(int keyslot, char **key, size_t *keysize,
|
||||||
|
size_t *integrity_keysize,
|
||||||
char **password, size_t *password_len,
|
char **password, size_t *password_len,
|
||||||
const char *prompt)
|
const char *prompt)
|
||||||
{
|
{
|
||||||
|
struct crypt_params_integrity ip = { 0 };
|
||||||
#ifdef HAVE_CRYPT_KEYSLOT_GET_PBKDF
|
#ifdef HAVE_CRYPT_KEYSLOT_GET_PBKDF
|
||||||
struct crypt_pbkdf_type pbkdf;
|
struct crypt_pbkdf_type pbkdf;
|
||||||
#endif
|
#endif
|
||||||
@@ -1388,7 +1395,20 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize,
|
|||||||
vkeysize = crypt_get_volume_key_size(g.cd);
|
vkeysize = crypt_get_volume_key_size(g.cd);
|
||||||
pr_verbose("Volume key size: %lu", vkeysize);
|
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)
|
if (rc != 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
@@ -1460,6 +1480,8 @@ static int open_keyslot(int keyslot, char **key, size_t *keysize,
|
|||||||
vkey = NULL;
|
vkey = NULL;
|
||||||
if (keysize != NULL)
|
if (keysize != NULL)
|
||||||
*keysize = vkeysize;
|
*keysize = vkeysize;
|
||||||
|
if (integrity_keysize != NULL)
|
||||||
|
*integrity_keysize = ip.integrity_key_size;
|
||||||
if (password != NULL)
|
if (password != NULL)
|
||||||
*password = pw;
|
*password = pw;
|
||||||
else
|
else
|
||||||
@@ -1482,24 +1504,31 @@ out:
|
|||||||
* Validate and get a secure key from a key slot. Optionally returns the key
|
* 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
|
* 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).
|
* 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,
|
static int validate_keyslot(int keyslot, char **key, size_t *keysize,
|
||||||
|
size_t *intgrity_keysize,
|
||||||
char **password, size_t *password_len,
|
char **password, size_t *password_len,
|
||||||
int *is_old_mk, size_t *clear_keysize,
|
int *is_old_mk, size_t *clear_keysize,
|
||||||
const char *prompt, const char *invalid_msg)
|
const char *prompt, const char *invalid_msg)
|
||||||
{
|
{
|
||||||
size_t vkeysize = 0;
|
size_t vkeysize = 0, ikeysize = 0;
|
||||||
char *vkey = NULL;
|
char *vkey = NULL;
|
||||||
int rc, is_old;
|
int rc, is_old;
|
||||||
|
|
||||||
rc = open_keyslot(keyslot, &vkey, &vkeysize, password, password_len,
|
rc = open_keyslot(keyslot, &vkey, &vkeysize, &ikeysize,
|
||||||
prompt);
|
password, password_len, prompt);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
keyslot = 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);
|
&is_old, NULL, g.verbose);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
if (invalid_msg != NULL)
|
if (invalid_msg != NULL)
|
||||||
@@ -1520,6 +1549,8 @@ static int validate_keyslot(int keyslot, char **key, size_t *keysize,
|
|||||||
vkey = NULL;
|
vkey = NULL;
|
||||||
if (keysize != NULL)
|
if (keysize != NULL)
|
||||||
*keysize = vkeysize;
|
*keysize = vkeysize;
|
||||||
|
if (intgrity_keysize != NULL)
|
||||||
|
*intgrity_keysize = ikeysize;
|
||||||
if (is_old_mk != NULL)
|
if (is_old_mk != NULL)
|
||||||
*is_old_mk = is_old;
|
*is_old_mk = is_old;
|
||||||
|
|
||||||
@@ -1539,8 +1570,10 @@ out:
|
|||||||
static int reencipher_prepare(int token)
|
static int reencipher_prepare(int token)
|
||||||
{
|
{
|
||||||
struct reencipher_token reenc_tok;
|
struct reencipher_token reenc_tok;
|
||||||
|
size_t integrity_keysize = 0;
|
||||||
struct vp_token vp_tok;
|
struct vp_token vp_tok;
|
||||||
char *password = NULL;
|
char *password = NULL;
|
||||||
|
size_t securekeysize;
|
||||||
size_t password_len;
|
size_t password_len;
|
||||||
char *key = NULL;
|
char *key = NULL;
|
||||||
size_t keysize;
|
size_t keysize;
|
||||||
@@ -1569,8 +1602,9 @@ static int reencipher_prepare(int token)
|
|||||||
}
|
}
|
||||||
|
|
||||||
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
|
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
|
||||||
rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize, &password,
|
rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize,
|
||||||
&password_len, &is_old_mk, NULL, prompt, NULL);
|
&integrity_keysize, &password, &password_len,
|
||||||
|
&is_old_mk, NULL, prompt, NULL);
|
||||||
free(prompt);
|
free(prompt);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto out;
|
goto out;
|
||||||
@@ -1581,7 +1615,9 @@ static int reencipher_prepare(int token)
|
|||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
goto out;
|
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,
|
reenc_tok.verification_pattern,
|
||||||
sizeof(reenc_tok.verification_pattern),
|
sizeof(reenc_tok.verification_pattern),
|
||||||
g.verbose);
|
g.verbose);
|
||||||
@@ -1624,7 +1660,7 @@ static int reencipher_prepare(int token)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (g.fromold) {
|
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,
|
NULL, REENCIPHER_OLD_TO_CURRENT,
|
||||||
&selected, g.verbose);
|
&selected, g.verbose);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
@@ -1637,7 +1673,7 @@ static int reencipher_prepare(int token)
|
|||||||
warnx("Failed to re-encipher the secure volume "
|
warnx("Failed to re-encipher the secure volume "
|
||||||
"key for device '%s'\n", g.pos_arg);
|
"key for device '%s'\n", g.pos_arg);
|
||||||
if (!selected &&
|
if (!selected &&
|
||||||
!is_ep11_aes_key((u8 *)key, keysize))
|
!is_ep11_aes_key((u8 *)key, securekeysize))
|
||||||
print_msg_for_cca_envvars(
|
print_msg_for_cca_envvars(
|
||||||
"secure AES volume key");
|
"secure AES volume key");
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
@@ -1647,7 +1683,7 @@ static int reencipher_prepare(int token)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (g.tonew) {
|
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,
|
NULL, REENCIPHER_CURRENT_TO_NEW,
|
||||||
&selected, g.verbose);
|
&selected, g.verbose);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
@@ -1660,7 +1696,7 @@ static int reencipher_prepare(int token)
|
|||||||
warnx("Failed to re-encipher the secure volume "
|
warnx("Failed to re-encipher the secure volume "
|
||||||
"key for device '%s'\n", g.pos_arg);
|
"key for device '%s'\n", g.pos_arg);
|
||||||
if (!selected &&
|
if (!selected &&
|
||||||
!is_ep11_aes_key((u8 *)key, keysize))
|
!is_ep11_aes_key((u8 *)key, securekeysize))
|
||||||
print_msg_for_cca_envvars(
|
print_msg_for_cca_envvars(
|
||||||
"secure AES volume key");
|
"secure AES volume key");
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
@@ -1727,8 +1763,10 @@ out:
|
|||||||
static int reencipher_complete(int token)
|
static int reencipher_complete(int token)
|
||||||
{
|
{
|
||||||
char vp[VERIFICATION_PATTERN_LEN];
|
char vp[VERIFICATION_PATTERN_LEN];
|
||||||
|
size_t integrity_keysize = 0;
|
||||||
struct reencipher_token tok;
|
struct reencipher_token tok;
|
||||||
char *password = NULL;
|
char *password = NULL;
|
||||||
|
size_t securekeysize;
|
||||||
size_t password_len;
|
size_t password_len;
|
||||||
char *key = NULL;
|
char *key = NULL;
|
||||||
size_t keysize;
|
size_t keysize;
|
||||||
@@ -1751,8 +1789,10 @@ static int reencipher_complete(int token)
|
|||||||
g.pos_arg);
|
g.pos_arg);
|
||||||
util_asprintf(&prompt, "Enter passphrase for key slot %d of '%s': ",
|
util_asprintf(&prompt, "Enter passphrase for key slot %d of '%s': ",
|
||||||
tok.original_keyslot, g.pos_arg);
|
tok.original_keyslot, g.pos_arg);
|
||||||
rc = validate_keyslot(tok.unbound_keyslot, &key, &keysize, &password,
|
rc = validate_keyslot(tok.unbound_keyslot, &key, &keysize,
|
||||||
&password_len, &is_old_mk, NULL, prompt, msg);
|
&integrity_keysize, &password, &password_len,
|
||||||
|
&is_old_mk, NULL,
|
||||||
|
prompt, msg);
|
||||||
free(msg);
|
free(msg);
|
||||||
free(prompt);
|
free(prompt);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
@@ -1762,6 +1802,8 @@ static int reencipher_complete(int token)
|
|||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
securekeysize = keysize - integrity_keysize;
|
||||||
|
|
||||||
if (is_old_mk) {
|
if (is_old_mk) {
|
||||||
util_asprintf(&msg, "The re-enciphered secure volume key "
|
util_asprintf(&msg, "The re-enciphered secure volume key "
|
||||||
"of device '%s' is enciphered with the "
|
"of device '%s' is enciphered with the "
|
||||||
@@ -1781,7 +1823,7 @@ static int reencipher_complete(int token)
|
|||||||
goto out;
|
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,
|
NULL, REENCIPHER_OLD_TO_CURRENT,
|
||||||
&selected, g.verbose);
|
&selected, g.verbose);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
@@ -1794,7 +1836,7 @@ static int reencipher_complete(int token)
|
|||||||
warnx("Failed to re-encipher the secure volume "
|
warnx("Failed to re-encipher the secure volume "
|
||||||
"key for device '%s'\n", g.pos_arg);
|
"key for device '%s'\n", g.pos_arg);
|
||||||
if (!selected &&
|
if (!selected &&
|
||||||
!is_ep11_aes_key((u8 *)key, keysize))
|
!is_ep11_aes_key((u8 *)key, securekeysize))
|
||||||
print_msg_for_cca_envvars(
|
print_msg_for_cca_envvars(
|
||||||
"secure AES volume key");
|
"secure AES volume key");
|
||||||
rc = -EINVAL;
|
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);
|
sizeof(vp), g.verbose);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
warnx("Failed to generate the verification pattern: %s",
|
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;
|
int reenc_pending = 0, vp_tok_avail = 0, is_valid = 0, is_old_mk = 0;
|
||||||
struct reencipher_token reenc_tok;
|
struct reencipher_token reenc_tok;
|
||||||
|
size_t integrity_keysize = 0;
|
||||||
struct vp_token vp_tok;
|
struct vp_token vp_tok;
|
||||||
const char *key_type;
|
const char *key_type;
|
||||||
u8 mkvp[MKVP_LENGTH];
|
u8 mkvp[MKVP_LENGTH];
|
||||||
size_t clear_keysize;
|
size_t clear_keysize;
|
||||||
size_t keysize = 0;
|
size_t keysize = 0;
|
||||||
|
size_t seckeysize;
|
||||||
char *key = NULL;
|
char *key = NULL;
|
||||||
char *prompt;
|
char *prompt;
|
||||||
char *msg;
|
char *msg;
|
||||||
@@ -1932,7 +1976,8 @@ static int command_validate(void)
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
|
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);
|
free(prompt);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto out;
|
goto out;
|
||||||
@@ -1941,8 +1986,10 @@ static int command_validate(void)
|
|||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
rc = validate_secure_key(g.pkey_fd, (u8 *)key, keysize, &clear_keysize,
|
seckeysize = keysize - integrity_keysize;
|
||||||
&is_old_mk, NULL, g.verbose);
|
|
||||||
|
rc = validate_secure_key(g.pkey_fd, (u8 *)key, seckeysize,
|
||||||
|
&clear_keysize, &is_old_mk, NULL, g.verbose);
|
||||||
is_valid = (rc == 0);
|
is_valid = (rc == 0);
|
||||||
|
|
||||||
token = find_token(g.cd, PAES_REENC_TOKEN_NAME);
|
token = find_token(g.cd, PAES_REENC_TOKEN_NAME);
|
||||||
@@ -1959,7 +2006,7 @@ static int command_validate(void)
|
|||||||
vp_tok_avail = 1;
|
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);
|
mkvp, g.verbose);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
warnx("Failed to get the master key verification pattern: %s",
|
warnx("Failed to get the master key verification pattern: %s",
|
||||||
@@ -1967,13 +2014,13 @@ static int command_validate(void)
|
|||||||
goto out;
|
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("Validation of secure volume key of device '%s':\n", g.pos_arg);
|
||||||
printf(" Status: %s\n", is_valid ? "Valid" : "Invalid");
|
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",
|
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);
|
printf(" Key type: %s\n", key_type);
|
||||||
if (is_valid) {
|
if (is_valid) {
|
||||||
printf(" Clear key size: %lu bits\n", clear_keysize);
|
printf(" Clear key size: %lu bits\n", clear_keysize);
|
||||||
@@ -2033,6 +2080,7 @@ out:
|
|||||||
*/
|
*/
|
||||||
static int command_setvp(void)
|
static int command_setvp(void)
|
||||||
{
|
{
|
||||||
|
size_t integrity_keysize = 0;
|
||||||
struct vp_token vp_tok;
|
struct vp_token vp_tok;
|
||||||
size_t keysize = 0;
|
size_t keysize = 0;
|
||||||
char *key = NULL;
|
char *key = NULL;
|
||||||
@@ -2041,8 +2089,9 @@ static int command_setvp(void)
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
|
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
|
||||||
rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize, NULL, NULL,
|
rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize,
|
||||||
NULL, NULL, prompt, NULL);
|
&integrity_keysize, NULL, NULL, NULL, NULL,
|
||||||
|
prompt, NULL);
|
||||||
free(prompt);
|
free(prompt);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto out;
|
goto out;
|
||||||
@@ -2053,7 +2102,8 @@ static int command_setvp(void)
|
|||||||
|
|
||||||
token = find_token(g.cd, PAES_VP_TOKEN_NAME);
|
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,
|
vp_tok.verification_pattern,
|
||||||
sizeof(vp_tok.verification_pattern),
|
sizeof(vp_tok.verification_pattern),
|
||||||
g.verbose);
|
g.verbose);
|
||||||
@@ -2084,7 +2134,9 @@ out:
|
|||||||
*/
|
*/
|
||||||
static int command_setkey(void)
|
static int command_setkey(void)
|
||||||
{
|
{
|
||||||
|
struct crypt_params_integrity ip = { 0 };
|
||||||
char vp[VERIFICATION_PATTERN_LEN];
|
char vp[VERIFICATION_PATTERN_LEN];
|
||||||
|
size_t integrity_keysize = 0;
|
||||||
size_t password_len = 0;
|
size_t password_len = 0;
|
||||||
struct vp_token vp_tok;
|
struct vp_token vp_tok;
|
||||||
size_t newkey_size = 0;
|
size_t newkey_size = 0;
|
||||||
@@ -2108,11 +2160,17 @@ static int command_setkey(void)
|
|||||||
if (newkey == NULL)
|
if (newkey == NULL)
|
||||||
return EXIT_FAILURE;
|
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)
|
if (rc != 0)
|
||||||
goto out;
|
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);
|
&is_old_mk, NULL, g.verbose);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
warnx("The secure key in file '%s' is not valid",
|
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);
|
util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg);
|
||||||
rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, &password,
|
rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, &integrity_keysize,
|
||||||
&password_len, prompt);
|
&password, &password_len, prompt);
|
||||||
free(prompt);
|
free(prompt);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto out;
|
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 "
|
warnx("The secure key in file '%s' is equal to the current "
|
||||||
"volume key, setkey is ignored", g.master_key_file);
|
"volume key, setkey is ignored", g.master_key_file);
|
||||||
rc = 0;
|
rc = 0;
|
||||||
goto out;
|
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,
|
rc = generate_key_verification_pattern(newkey,
|
||||||
sizeof(vp), g.verbose);
|
newkey_size - integrity_keysize,
|
||||||
|
vp, sizeof(vp), g.verbose);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
warnx("Failed to generate the verification pattern: %s",
|
warnx("Failed to generate the verification pattern: %s",
|
||||||
strerror(-rc));
|
strerror(-rc));
|
||||||
|
|||||||
Reference in New Issue
Block a user