mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
940ce67900 | ||
|
|
c12faf0128 | ||
|
|
a3d50487ed | ||
|
|
b1d8e40bfe | ||
|
|
8d3949f77b | ||
|
|
2f49025d49 | ||
|
|
4c9ceeeb64 | ||
|
|
3a0c394fa2 | ||
|
|
20a4ebd83c | ||
|
|
4a9f66fc31 | ||
|
|
35d68b2101 | ||
|
|
522c67a2c3 | ||
|
|
f4ee6c2c8d | ||
|
|
377f2a59f5 | ||
|
|
176f0405b8 | ||
|
|
839d34e758 | ||
|
|
a68d1e323b | ||
|
|
549c7ef9e7 | ||
|
|
103c9444c9 | ||
|
|
fe7bef3f09 | ||
|
|
8a88ada9c5 | ||
|
|
538bc7b4e8 | ||
|
|
b56451e5a4 | ||
|
|
63f9a672d0 | ||
|
|
2c5c5ef4b4 | ||
|
|
5a3ffc2036 | ||
|
|
e576443859 | ||
|
|
43067df78e | ||
|
|
973059438f | ||
|
|
9d3567d9ce | ||
|
|
7687b0fcda | ||
|
|
6f70f34b79 | ||
|
|
37aed81745 | ||
|
|
5aa1824eef | ||
|
|
82603e589a | ||
|
|
3ba897a1be | ||
|
|
fa92a7f7c9 | ||
|
|
f569849e57 | ||
|
|
458c27a0e1 | ||
|
|
e4dcf084c5 | ||
|
|
bed1bc8676 | ||
|
|
8e315c4401 | ||
|
|
ae8fa4371f | ||
|
|
29ff9408e0 | ||
|
|
a24be25779 | ||
|
|
58337d7f90 | ||
|
|
fcaad5a8e2 | ||
|
|
4d598ade86 | ||
|
|
b455ee9a31 | ||
|
|
82b9328e48 | ||
|
|
f45a45c38c | ||
|
|
d1bf9e68c6 | ||
|
|
0356d78bc2 | ||
|
|
a4f48415a0 | ||
|
|
c9f0e3accf | ||
|
|
9dd432ca94 | ||
|
|
179bc57cdf | ||
|
|
e1d1c6df9c | ||
|
|
e894fb61d8 | ||
|
|
c3a84109b7 | ||
|
|
1ca5f63dee | ||
|
|
60bc1e38d0 | ||
|
|
431e4542ca | ||
|
|
731f00202c | ||
|
|
2945593991 | ||
|
|
f375327085 | ||
|
|
338bea49c2 | ||
|
|
d15e36cf24 | ||
|
|
4b76530788 | ||
|
|
cb1c002bed | ||
|
|
d6f7eff5b5 | ||
|
|
e1692b7079 | ||
|
|
54b3c9ef24 | ||
|
|
48743aa28b | ||
|
|
7ce988ac59 | ||
|
|
f4fec5f120 | ||
|
|
2739b8a488 | ||
|
|
ce9c518b97 | ||
|
|
bd2a2cc6a1 | ||
|
|
6547d1ae4c | ||
|
|
650265e296 | ||
|
|
b122acb422 | ||
|
|
9710f29770 | ||
|
|
ed01bac8e2 | ||
|
|
66d3aa849a | ||
|
|
95e4b3413a | ||
|
|
06e0d569f1 |
@@ -2,7 +2,7 @@
|
||||
exclude: \.(bin|crl|crt|key)$
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: check-merge-conflict
|
||||
- id: end-of-file-fixer
|
||||
@@ -11,6 +11,9 @@ repos:
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-shebang-scripts-are-executable
|
||||
exclude_types: ['rust']
|
||||
- id: check-yaml
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: git-clang-format
|
||||
@@ -24,7 +27,7 @@ repos:
|
||||
minimum_pre_commit_version: "2.9.0"
|
||||
types_or: [c++, c]
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.2.1
|
||||
rev: v2.4.1
|
||||
hooks:
|
||||
- id: codespell
|
||||
exclude_types: ['rust']
|
||||
|
||||
@@ -110,6 +110,7 @@ List of all individuals having contributed content to s390-tools
|
||||
- Raimund Schroeder
|
||||
- Ralph Wuerthner
|
||||
- Rene Trumpp
|
||||
- Richie Buturla
|
||||
- Rolf Schaefer
|
||||
- Sa Liu
|
||||
- Sascha Silbe
|
||||
|
||||
32
CHANGELOG.md
32
CHANGELOG.md
@@ -1,6 +1,38 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
|
||||
* __v2.39.0 (2025-10-14)__
|
||||
|
||||
For Linux kernel version: 6.16 / 6.17
|
||||
|
||||
Changes of existing tools:
|
||||
- chpstat: Add options to select IEC units for scaling (SI units are default)
|
||||
- chzdev: Introduce --no-module-load option
|
||||
- cpi: Disable CPI for SEL guests by default
|
||||
- dbginfo.sh: Enhance logging on timeout triggered
|
||||
- iucvterm: Install symlink for lsiucvallow.8 man page
|
||||
- lshwc: Add command line flag to specify individual counters
|
||||
- lspai: Add command line flag for delta values
|
||||
- lspai: Add command line flag for short counter names
|
||||
- lspai: Add command line flag to specify individual counters
|
||||
- lspai: Add command line flags for all cpus
|
||||
- lspai: Add command line flags for hexadecimal output
|
||||
- man: Use CR for constant width font
|
||||
- pvimg: Add '--image-key' option
|
||||
- zdev: Allow dynamic control of module load
|
||||
- zipl/boot: Fix EBCDIC code page 500 conversion and decrease size by 200 bytes
|
||||
- zipl: Add support of heterogeneous mirrors (remove technical limitations
|
||||
on mirrored targets, thus allowing mirrored devices consist of partitions
|
||||
at different offsets on disks of different types and geometry).
|
||||
- zkey: Add support for generating and importing exportable secure keys
|
||||
|
||||
Bug Fixes:
|
||||
- chpstat: Fix scaling of DPU utilization calculation
|
||||
- zdev/dracut: Prevent loading of unused kernel modules
|
||||
- zdev: Fix double device configuration on DPM systems
|
||||
- zdev: Fix double device configuration with rd.dasd
|
||||
- zipl_helper.device-mapper: Fix segfault in an error path
|
||||
|
||||
* __v2.38.0 (2025-06-25)__
|
||||
|
||||
For Linux kernel version: 6.14 / 6.15
|
||||
|
||||
12
README.md
12
README.md
@@ -414,6 +414,18 @@ the different tools are provided:
|
||||
dumping).
|
||||
Removing the inflate modules and function prototypes are the only major
|
||||
modifications made to the kernel version of zlib.
|
||||
For stand-alone NVMe and list-directed DASD dump, the following tools/packages
|
||||
are required to populate dumper's initramfs:
|
||||
- cat
|
||||
- cut
|
||||
- findmnt
|
||||
- logsave
|
||||
- makedumpfile
|
||||
- mount
|
||||
- poweroff
|
||||
- sha256sum
|
||||
- sync
|
||||
- umount
|
||||
|
||||
* zgetdump
|
||||
For building zgetdump you need OpenSSL version 1.1.0 or newer
|
||||
|
||||
@@ -32,7 +32,7 @@ endif
|
||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||
VERSION := 2
|
||||
RELEASE := 38
|
||||
RELEASE := 39
|
||||
PATCHLEVEL := 0
|
||||
DISTRELEASE := build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
|
||||
@@ -22,7 +22,7 @@ ALL_CPPFLAGS += -DVERSION=$(VERSION)
|
||||
all: check_dep cpacfstats cpacfstatsd
|
||||
|
||||
cpacfstatsd: cpacfstatsd.o stats_sock.o perf_crypto.o cpacfstats_common.o \
|
||||
$(rootdir)/libutil/libutil.a
|
||||
$(rootdir)/libcpumf/libcpumf.a $(rootdir)/libutil/libutil.a
|
||||
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -ludev -lpthread -o $@
|
||||
|
||||
cpacfstats: cpacfstats.o stats_sock.o cpacfstats_common.o
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <libudev.h>
|
||||
|
||||
#include "cpacfstats.h"
|
||||
#include "lib/libcpumf.h"
|
||||
#include "../include/lib/zt_common.h"
|
||||
|
||||
/* correlation between counter and perf counter string */
|
||||
@@ -123,16 +124,6 @@ static struct percpucounter *findcpu(unsigned int cpunum, int unlinkflag)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
|
||||
int cpu, int group_fd, unsigned long flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
|
||||
group_fd, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int perf_supported(void)
|
||||
{
|
||||
return !access("/proc/sys/kernel/perf_event_paranoid", R_OK);
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
static bool actions[ACTION_SAMPLE + 1]; /* Specified command line options */
|
||||
|
||||
/* This defines the number of pages a Sample Data Buffer Table (SDBT) can hold
|
||||
* as payload data. Each SDBT is one PAGE (4096 bytes) and continas 512 eight
|
||||
* as payload data. Each SDBT is one PAGE (4096 bytes) and contains 512 eight
|
||||
* byte data pointers to Sample Data Buffers (SDB). The last entry of a SDBT
|
||||
* points to another SDBT and can not store payload.
|
||||
*/
|
||||
@@ -139,7 +139,7 @@ static struct samples { /* Sample definition for all machines */
|
||||
* The CPU Measurement facility has a first and second version number.
|
||||
*
|
||||
* The first version number governs basic counter set and the
|
||||
* problem state counter set. Currently used are first verion numbers 1 and 3.
|
||||
* problem state counter set. Currently used are first version numbers 1 and 3.
|
||||
* The counter numbers are identifical for version number 1 and 3, but
|
||||
* have different purpose and description.
|
||||
*
|
||||
@@ -627,7 +627,7 @@ static struct counters cpumcf_z10_counters[] = {
|
||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||
.name = "L1D_L3_LOCAL_WRITES",
|
||||
.desc = "A directory write to the Level-1 D-Cache directory"
|
||||
"\n\t\twhere the installtion cache line was source from"
|
||||
"\n\t\twhere the installation cache line was source from"
|
||||
"\n\t\tthe Level-3 cache that is on the same book as the"
|
||||
"\n\t\tData cache (Local L2 cache)",
|
||||
},
|
||||
@@ -1916,7 +1916,7 @@ static struct counters cpumcf_z14_counters[] = {
|
||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||
.name = "L1I_ONCHIP_L3_SOURCED_WRITES",
|
||||
.desc = "A directory write to the Level-1 Instruction cache"
|
||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
||||
"\n\t\tdirectory where the returned cache line was sourced"
|
||||
"\n\t\tfrom an On-Chip Level-3 cache without intervention",
|
||||
},
|
||||
{
|
||||
@@ -1924,7 +1924,7 @@ static struct counters cpumcf_z14_counters[] = {
|
||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||
.name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES",
|
||||
.desc = "A directory write to the Level-1 Instruction cache"
|
||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
||||
"\n\t\tdirectory where the returned cache line was sourced"
|
||||
"\n\t\tfrom On-Chip memory",
|
||||
},
|
||||
{
|
||||
@@ -1932,7 +1932,7 @@ static struct counters cpumcf_z14_counters[] = {
|
||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||
.name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV",
|
||||
.desc = "A directory write to the Level-1 Instruction cache"
|
||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
||||
"\n\t\tdirectory where the returned cache line was sourced"
|
||||
"\n\t\tfrom an On-Chip Level-3 cache with intervention",
|
||||
},
|
||||
{
|
||||
@@ -2342,7 +2342,7 @@ static struct counters cpumcf_z15_counters[] = {
|
||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||
.name = "L1I_ONCHIP_L3_SOURCED_WRITES",
|
||||
.desc = "A directory write to the Level-1 Instruction cache"
|
||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
||||
"\n\t\tdirectory where the returned cache line was sourced"
|
||||
"\n\t\tfrom an On-Chip Level-3 cache without intervention",
|
||||
},
|
||||
{
|
||||
@@ -2350,7 +2350,7 @@ static struct counters cpumcf_z15_counters[] = {
|
||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||
.name = "L1I_ONCHIP_MEMORY_SOURCED_WRITES",
|
||||
.desc = "A directory write to the Level-1 Instruction cache"
|
||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
||||
"\n\t\tdirectory where the returned cache line was sourced"
|
||||
"\n\t\tfrom On-Chip memory",
|
||||
},
|
||||
{
|
||||
@@ -2358,7 +2358,7 @@ static struct counters cpumcf_z15_counters[] = {
|
||||
.ctrset = CPUMF_CTRSET_EXTENDED,
|
||||
.name = "L1I_ONCHIP_L3_SOURCED_WRITES_IV",
|
||||
.desc = "A directory write to the Level-1 Instruction cache"
|
||||
"\n\t\tdirectory where the returned cache ine was sourced"
|
||||
"\n\t\tdirectory where the returned cache line was sourced"
|
||||
"\n\t\tfrom an On-Chip Level-3 cache with intervention",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_str.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_fmt.h"
|
||||
#include "lib/libcpumf.h"
|
||||
@@ -61,6 +61,7 @@ static bool hideundef;
|
||||
static bool delta, firstread;
|
||||
static int output_format = FMT_CSV;
|
||||
static bool quote_all;
|
||||
static char *ctrlist; /* Comma separated list of counter to extract */
|
||||
|
||||
static unsigned int max_possible_cpus; /* No of possible CPUs */
|
||||
static struct ctrname { /* List of defined counters */
|
||||
@@ -151,6 +152,8 @@ static bool read_counternames(void)
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < count && ctr >= 0; i++) {
|
||||
if (!ctr_in_list(namelist[i]->d_name, ctrlist))
|
||||
continue;
|
||||
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
|
||||
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1)
|
||||
ctrname[ctr].name = mk_name(ctr, namelist[i]->d_name);
|
||||
@@ -779,6 +782,12 @@ static struct util_opt opt_vec[] = {
|
||||
.argument = "FORMAT",
|
||||
.desc = "List counters in specified FORMAT (" FMT_TYPE_NAMES ")"
|
||||
},
|
||||
{
|
||||
.option = { "counters", required_argument, NULL, 'c' },
|
||||
.argument = "LIST",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "Specify comma separated list of counters to display"
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
@@ -892,6 +901,12 @@ int main(int argc, char **argv)
|
||||
errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES);
|
||||
output_format = fmt;
|
||||
break;
|
||||
case 'c':
|
||||
hideundef = true;
|
||||
ctrlist = util_strdup(optarg);
|
||||
util_str_rm_whitespace(optarg, ctrlist);
|
||||
util_str_toupper(ctrlist);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,5 +942,6 @@ int main(int argc, char **argv)
|
||||
free_counternames();
|
||||
free(check);
|
||||
free(ioctlbuffer);
|
||||
free(ctrlist);
|
||||
return ch;
|
||||
}
|
||||
|
||||
495
cpumf/lspai.c
495
cpumf/lspai.c
@@ -6,7 +6,6 @@
|
||||
|
||||
/* List available Processor Assist Instrumentation (PAI) counters. */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
@@ -17,7 +16,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/time.h>
|
||||
#include <linux/perf_event.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_file.h"
|
||||
@@ -28,26 +32,68 @@
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_scandir.h"
|
||||
#include "lib/util_str.h"
|
||||
#include "lib/libcpumf.h"
|
||||
|
||||
#define OPT_FORMAT 256 /* --format XXX option */
|
||||
#define STR_SUB(x) #x
|
||||
#define STR(x) STR_SUB(x)
|
||||
|
||||
#define OPT_FORMAT 256 /* --format XXX option */
|
||||
#define DEFAULT_LOOP_INTERVAL 60 /* loop interval in seconds */
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
.option = { "all", no_argument, NULL, 'a' },
|
||||
.desc = "Displays all CPUs in output"
|
||||
},
|
||||
{
|
||||
.option = { "delta", no_argument, NULL, 'd' },
|
||||
.desc = "Display delta counter values"
|
||||
},
|
||||
{
|
||||
.option = { "counters", required_argument, NULL, 'c' },
|
||||
.argument = "LIST",
|
||||
.desc = "Specify comma separated list of counters to display"
|
||||
},
|
||||
{
|
||||
.option = { "format", required_argument, NULL, OPT_FORMAT },
|
||||
.argument = "FORMAT",
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.desc = "List counters in specified FORMAT (" FMT_TYPE_NAMES ")"
|
||||
},
|
||||
{
|
||||
.option = { "loops", required_argument, NULL, 'l' },
|
||||
.argument = "COUNT",
|
||||
.desc = "Number of read operations"
|
||||
},
|
||||
{
|
||||
.option = { "interval", required_argument, NULL, 'i' },
|
||||
.argument = "SECONDS",
|
||||
.desc = "Time to wait between loop iterations (default "
|
||||
STR(DEFAULT_LOOP_INTERVAL) "s)"
|
||||
},
|
||||
{
|
||||
.option = { "numeric", no_argument, NULL, 'n' },
|
||||
.desc = "Sort PAI counters by counter number"
|
||||
},
|
||||
{
|
||||
.option = { "short", no_argument, NULL, 's' },
|
||||
.desc = "Abbreviate counter name with counter set letter and number"
|
||||
},
|
||||
{
|
||||
.option = { "type", required_argument, NULL, 't' },
|
||||
.argument = "TYPE",
|
||||
.desc = "Type of PAI counters to show: crypto, nnpa"
|
||||
},
|
||||
{
|
||||
.option = { "hex0x", no_argument, NULL, 'X' },
|
||||
.desc = "Counter values in hexadecimal format with leading 0x"
|
||||
},
|
||||
{
|
||||
.option = { "hex", no_argument, NULL, 'x' },
|
||||
.desc = "Counter values in hexadecimal format"
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
@@ -59,14 +105,24 @@ static const struct util_prg prg = {
|
||||
{
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2023,
|
||||
.pub_last = 2023,
|
||||
.pub_last = 2025,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END
|
||||
}
|
||||
};
|
||||
|
||||
static bool allcpu; /* Show counter values per CPU */
|
||||
static bool numsort; /* If true sort counter numerically */
|
||||
static bool shortname; /* Use abbreviated counter names */
|
||||
static bool delta, firstread; /* Display delta values */
|
||||
static int output_format = -1; /* Generate style if >= 0 */
|
||||
static unsigned int max_cpus; /* # of CPUs to read counter values from */
|
||||
static unsigned int max_fds; /* # of file descriptor to read counter values */
|
||||
static unsigned long loops; /* # loops */
|
||||
static unsigned long read_interval = DEFAULT_LOOP_INTERVAL;
|
||||
static cpu_set_t cpu_online_mask;
|
||||
static char *ctrformat = "%ld"; /* Default counter output format */
|
||||
static char *ctrlist; /* Comma separated list of counter to extract */
|
||||
|
||||
#define PAI_PATH "/bus/event_source/devices/%s"
|
||||
|
||||
@@ -78,9 +134,18 @@ enum pai_types { /* Bit mask for supported PAI counters */
|
||||
|
||||
static int pai_types_show;
|
||||
|
||||
struct pai_cpudata { /* Event data per CPU */
|
||||
int fd; /* Event file descriptor */
|
||||
int cpu; /* CPU number */
|
||||
unsigned long value; /* Event value */
|
||||
unsigned long prev_value; /* Previous value for deltas */
|
||||
};
|
||||
|
||||
struct pai_ctrname { /* List of defined counters */
|
||||
char *name; /* Counter name */
|
||||
unsigned long nr; /* Counter number */
|
||||
unsigned long total; /* Total count on all CPus */
|
||||
struct pai_cpudata *data; /* Counter data per CPU */
|
||||
};
|
||||
|
||||
struct pai_node { /* Head for PAI counter sets */
|
||||
@@ -113,6 +178,20 @@ static unsigned long pai_type_base(enum pai_types t)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return character for this counter set. */
|
||||
static char pai_type_char(enum pai_types t)
|
||||
{
|
||||
switch (t) {
|
||||
case pai_type_crypto:
|
||||
return 'C';
|
||||
case pai_type_nnpa:
|
||||
return 'N';
|
||||
case pai_type_max:
|
||||
break;
|
||||
}
|
||||
return 'U';
|
||||
}
|
||||
|
||||
/* Test PAI counter name from command line option. */
|
||||
static const char *pai_type_name(enum pai_types t)
|
||||
{
|
||||
@@ -154,16 +233,6 @@ static int pai_ctrcmp(const void *p1, const void *p2)
|
||||
return l->nr > r->nr ? 1 : -1;
|
||||
}
|
||||
|
||||
/* Convert string to upper case. */
|
||||
static char *str2uc(const char *s)
|
||||
{
|
||||
char *uc = util_strdup(s), *old_uc = uc;
|
||||
|
||||
for (; *uc; ++uc)
|
||||
*uc = toupper(*uc);
|
||||
return old_uc;
|
||||
}
|
||||
|
||||
/* Read counter names and assigned event number from sysfs file tree.
|
||||
* Exit when sysfs directory can not be scanned.
|
||||
*/
|
||||
@@ -171,7 +240,7 @@ static void read_counternames(struct pai_node *node)
|
||||
{
|
||||
int i, more = 0, ctr = 0, count = 0;
|
||||
struct dirent **namelist = NULL;
|
||||
char *path, *ctrpath;
|
||||
char *path, *ctrpath, sname[128];
|
||||
|
||||
/* Read counter names and assigned event number. */
|
||||
path = util_path_sysfs(PAI_PATH "/events", node->sysfs_name);
|
||||
@@ -184,9 +253,18 @@ static void read_counternames(struct pai_node *node)
|
||||
for (i = 0; i < count && ctr >= 0; i++) {
|
||||
util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
|
||||
if (util_file_read_va(ctrpath, "event=%x", &ctr) == 1) {
|
||||
snprintf(sname, sizeof(sname), "%c%ld",
|
||||
pai_type_char(node->type), ctr - node->base);
|
||||
if (!ctr_in_list(sname, ctrlist) &&
|
||||
!ctr_in_list(namelist[i]->d_name, ctrlist)) {
|
||||
/* Counter not listed in --counters option */
|
||||
continue;
|
||||
}
|
||||
node->ctrlist[node->ctridx].data = NULL;
|
||||
node->ctrlist[node->ctridx].name = util_strdup(namelist[i]->d_name);
|
||||
node->ctrlist[node->ctridx++].nr = ctr;
|
||||
more++;
|
||||
max_fds++;
|
||||
} else {
|
||||
warnx("Cannot parse %s", ctrpath);
|
||||
}
|
||||
@@ -199,7 +277,7 @@ static void read_counternames(struct pai_node *node)
|
||||
qsort(node->ctrlist, more, sizeof(*node->ctrlist), pai_ctrcmp);
|
||||
}
|
||||
|
||||
static void show_format(enum util_fmt_t fmt)
|
||||
static void format_painode(enum util_fmt_t fmt)
|
||||
{
|
||||
struct pai_node *node;
|
||||
|
||||
@@ -214,9 +292,12 @@ static void show_format(enum util_fmt_t fmt)
|
||||
for (int i = 0; i < node->ctridx; ++i) {
|
||||
util_fmt_obj_start(FMT_ROW, "counter");
|
||||
util_fmt_pair(FMT_QUOTE, "name", "%s", node->ctrlist[i].name);
|
||||
if (shortname)
|
||||
util_fmt_pair(FMT_QUOTE, "name", "%c%d",
|
||||
pai_type_char(node->type),
|
||||
node->ctrlist[i].nr - node->base);
|
||||
util_fmt_pair(FMT_DEFAULT, "config", "%d", node->ctrlist[i].nr);
|
||||
util_fmt_pair(FMT_DEFAULT, "number", "%d",
|
||||
node->ctrlist[i].nr - node->base);
|
||||
util_fmt_pair(FMT_DEFAULT, "id", "%d", node->ctrlist[i].nr - node->base);
|
||||
util_fmt_obj_end();
|
||||
}
|
||||
util_fmt_obj_end(); /* Counters */
|
||||
@@ -226,14 +307,14 @@ static void show_format(enum util_fmt_t fmt)
|
||||
util_fmt_exit();
|
||||
}
|
||||
|
||||
static void show_painode(void)
|
||||
static void list_painode(void)
|
||||
{
|
||||
struct pai_node *node;
|
||||
int indent = 0;
|
||||
int offset = 0;
|
||||
|
||||
if (output_format != -1) {
|
||||
show_format(output_format);
|
||||
format_painode(output_format);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -264,8 +345,10 @@ static void free_painode(void)
|
||||
|
||||
util_list_iterate_safe(&pai_list, node, next) {
|
||||
free(node->name_uc);
|
||||
for (int i = 0; i < node->ctridx; ++i)
|
||||
for (int i = 0; i < node->ctridx; ++i) {
|
||||
free(node->ctrlist[i].name);
|
||||
free(node->ctrlist[i].data);
|
||||
}
|
||||
free(node->ctrlist);
|
||||
free(node);
|
||||
}
|
||||
@@ -279,7 +362,8 @@ static void make_painode(enum pai_types t)
|
||||
node->type = t;
|
||||
node->sysfs_name = pai_type_sysfs(t);
|
||||
node->name = pai_type_name(t);
|
||||
node->name_uc = str2uc(node->name);
|
||||
node->name_uc = util_strdup(node->name);
|
||||
util_str_toupper(node->name_uc);
|
||||
node->filter_name = pai_type_filter(t);
|
||||
node->base = pai_type_base(t);
|
||||
|
||||
@@ -308,6 +392,267 @@ static void sort_painode(void)
|
||||
util_list_sort(&pai_list, painode_cmp, NULL);
|
||||
}
|
||||
|
||||
/* Read counter value. */
|
||||
static unsigned long event_read(int fd)
|
||||
{
|
||||
unsigned long count;
|
||||
int rc;
|
||||
|
||||
rc = read(fd, &count, sizeof(count));
|
||||
if (rc != sizeof(count))
|
||||
err(EXIT_FAILURE, "Failed to read counter value");
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Write header. */
|
||||
static void line_header(void)
|
||||
{
|
||||
struct pai_node *node;
|
||||
static bool header;
|
||||
bool comma = false;
|
||||
|
||||
if (header)
|
||||
return; /* Printed already */
|
||||
printf("Date,Time,CPU,"); /* Print counter name and number */
|
||||
util_list_iterate(&pai_list, node) {
|
||||
for (int i = 0; i < node->ctridx; ++i) {
|
||||
if (comma)
|
||||
putchar(',');
|
||||
if (shortname) {
|
||||
printf("%c%ld", pai_type_char(node->type),
|
||||
node->ctrlist[i].nr - node->base);
|
||||
} else {
|
||||
printf("%s(%ld)", node->ctrlist[i].name ?: node->name_uc,
|
||||
node->ctrlist[i].nr - node->base);
|
||||
}
|
||||
comma = true;
|
||||
}
|
||||
}
|
||||
putchar('\n');
|
||||
header = true;
|
||||
}
|
||||
|
||||
/* Print CPU specific counter values. */
|
||||
static void line_cpu(char *header)
|
||||
{
|
||||
struct pai_cpudata *data;
|
||||
struct pai_node *node;
|
||||
bool comma;
|
||||
char txt[16];
|
||||
|
||||
for (unsigned int h = 0; h < max_cpus; ++h) {
|
||||
comma = false;
|
||||
util_list_iterate(&pai_list, node) {
|
||||
for (int i = 0; i < node->ctridx; ++i) {
|
||||
data = &node->ctrlist[i].data[h];
|
||||
if (!comma) {
|
||||
snprintf(txt, sizeof(txt), "CPU%d,", data->cpu);
|
||||
printf("%s,%s", header, txt);
|
||||
} else {
|
||||
putchar(',');
|
||||
}
|
||||
printf(ctrformat, data->value);
|
||||
comma = true;
|
||||
}
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
/* Write an output line. */
|
||||
static void line_out(char *header)
|
||||
{
|
||||
struct pai_node *node;
|
||||
bool comma;
|
||||
|
||||
line_header();
|
||||
if (allcpu)
|
||||
line_cpu(header);
|
||||
|
||||
/* Print total count of all CPUs */
|
||||
printf("%s,%s,", header, delta && !firstread ? "Delta" : "Total");
|
||||
comma = false;
|
||||
util_list_iterate(&pai_list, node) {
|
||||
for (int i = 0; i < node->ctridx; ++i) {
|
||||
if (comma)
|
||||
putchar(',');
|
||||
printf(ctrformat, node->ctrlist[i].total);
|
||||
comma = true;
|
||||
}
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
/* Write a formatted line. */
|
||||
static void format_line_out(time_t now, char *now_text)
|
||||
{
|
||||
static unsigned int called;
|
||||
struct pai_node *node;
|
||||
char cpuid[16];
|
||||
|
||||
if (!called) {
|
||||
util_fmt_init(stdout, output_format, FMT_DEFAULT | FMT_HANDLEINT, 1);
|
||||
util_fmt_obj_start(FMT_DEFAULT, NULL);
|
||||
util_fmt_obj_start(FMT_LIST, "measurements");
|
||||
}
|
||||
util_list_iterate(&pai_list, node) {
|
||||
if (!node->ctridx) /* Counter set not selected */
|
||||
continue;
|
||||
util_fmt_obj_start(FMT_DEFAULT, "entry");
|
||||
util_fmt_pair(FMT_PERSIST, "iteration", "%d", called++);
|
||||
util_fmt_pair(FMT_PERSIST, "time_epoch", "%d", now);
|
||||
util_fmt_pair(FMT_QUOTE | FMT_PERSIST, "time", "%s", now_text);
|
||||
util_fmt_pair(FMT_QUOTE | FMT_PERSIST, "valuetype",
|
||||
(delta && !firstread) ? "delta" : "total");
|
||||
util_fmt_obj_start(FMT_LIST, "counters");
|
||||
for (int i = 0; i < node->ctridx; ++i) {
|
||||
util_fmt_obj_start(FMT_ROW, "counter");
|
||||
util_fmt_pair(FMT_QUOTE, "name", "%s", node->ctrlist[i].name);
|
||||
if (shortname)
|
||||
util_fmt_pair(FMT_QUOTE, "shortname", "%c%d",
|
||||
pai_type_char(node->type),
|
||||
node->ctrlist[i].nr - node->base);
|
||||
util_fmt_pair(FMT_DEFAULT, "config", "%d", node->ctrlist[i].nr);
|
||||
util_fmt_pair(FMT_DEFAULT, "id", "%d", node->ctrlist[i].nr - node->base);
|
||||
util_fmt_pair(FMT_DEFAULT, "value", ctrformat, node->ctrlist[i].total);
|
||||
if (allcpu) {
|
||||
for (unsigned int j = 0; j < max_cpus; ++j) {
|
||||
snprintf(cpuid, sizeof(cpuid), "cpu%d", j);
|
||||
util_fmt_pair(FMT_DEFAULT, cpuid, ctrformat,
|
||||
node->ctrlist[i].data[j].value);
|
||||
}
|
||||
}
|
||||
util_fmt_obj_end();
|
||||
}
|
||||
util_fmt_obj_end(); /* Counters */
|
||||
util_fmt_obj_end(); /* Entry */
|
||||
}
|
||||
}
|
||||
|
||||
/* Terminate formatted output. */
|
||||
static void format_line_end(void)
|
||||
{
|
||||
util_fmt_obj_end(); /* Iteration */
|
||||
util_fmt_obj_end(); /* Default */
|
||||
util_fmt_exit();
|
||||
}
|
||||
|
||||
/* Display counter values. */
|
||||
static void show_values(void)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
struct tm *now_tm;
|
||||
char now_text[32];
|
||||
|
||||
now_tm = localtime(&now);
|
||||
if (output_format != -1) {
|
||||
strftime(now_text, sizeof(now_text), "%F %T%z", now_tm);
|
||||
format_line_out(now, now_text);
|
||||
} else {
|
||||
strftime(now_text, sizeof(now_text), "%F,%T", now_tm);
|
||||
line_out(now_text);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read each counter value. */
|
||||
static void read_painode(void)
|
||||
{
|
||||
struct pai_cpudata *data;
|
||||
struct pai_node *node;
|
||||
unsigned long value;
|
||||
|
||||
util_list_iterate(&pai_list, node) {
|
||||
for (int i = 0; i < node->ctridx; ++i) {
|
||||
node->ctrlist[i].total = 0;
|
||||
for (size_t j = 0; j < max_cpus; ++j) {
|
||||
data = &node->ctrlist[i].data[j];
|
||||
value = event_read(data->fd);
|
||||
if (delta) {
|
||||
data->value = value - data->prev_value;
|
||||
data->prev_value = value;
|
||||
} else {
|
||||
data->value = value;
|
||||
}
|
||||
node->ctrlist[i].total += data->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wait_painode(void)
|
||||
{
|
||||
for (unsigned long i = 0; i < loops; ++i) {
|
||||
read_painode();
|
||||
show_values();
|
||||
if (i + 1 < loops)
|
||||
sleep(read_interval);
|
||||
firstread = false;
|
||||
}
|
||||
format_line_end();
|
||||
}
|
||||
|
||||
/* Install one event. */
|
||||
static int event_add(int cpu, int idx, struct pai_node *node)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
int fd;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
attr.size = sizeof(attr);
|
||||
attr.config = node->ctrlist[idx].nr;
|
||||
attr.type = node->pmu;
|
||||
fd = perf_event_open(&attr, -1, cpu, -1, 0);
|
||||
if (fd == -1)
|
||||
err(EXIT_FAILURE, "Failed to open perf event: file descriptor not available");
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Increase number of file descriptors this process can open. */
|
||||
static void event_fdlimit(void)
|
||||
{
|
||||
unsigned int needed = 3 + max_fds * max_cpus;
|
||||
struct rlimit rlimit;
|
||||
|
||||
if (getrlimit(RLIMIT_NOFILE, &rlimit) == -1)
|
||||
err(EXIT_FAILURE, "Failed to read RLIMIT_NOFILE");
|
||||
if (needed > rlimit.rlim_cur)
|
||||
rlimit.rlim_cur = needed;
|
||||
if (setrlimit(RLIMIT_NOFILE, &rlimit) == -1)
|
||||
err(EXIT_FAILURE, "Failed to set RLIMIT_NOFILE");
|
||||
}
|
||||
|
||||
/* Install all events and iterate over requested read operations. */
|
||||
static void event_painode(void)
|
||||
{
|
||||
size_t pai_cpudata_sz = sizeof(struct pai_cpudata) * max_cpus;
|
||||
struct pai_cpudata *data;
|
||||
struct pai_node *node;
|
||||
|
||||
event_fdlimit();
|
||||
util_list_iterate(&pai_list, node) {
|
||||
for (int i = 0; i < node->ctridx; ++i) {
|
||||
node->ctrlist[i].data = util_malloc(pai_cpudata_sz);
|
||||
data = node->ctrlist[i].data;
|
||||
for (unsigned int j = 0; j < CPU_SETSIZE; ++j) {
|
||||
if (CPU_ISSET(j, &cpu_online_mask)) {
|
||||
data->cpu = j;
|
||||
data->fd = event_add(j, i, node);
|
||||
data->value = 0;
|
||||
data->prev_value = 0;
|
||||
++data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
wait_painode();
|
||||
util_list_iterate(&pai_list, node) {
|
||||
for (int i = 0; i < node->ctridx; ++i) {
|
||||
for (unsigned int j = 0; j < max_cpus; ++j)
|
||||
close(node->ctrlist[i].data[j].fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for hardware support and return false if not available. */
|
||||
static bool have_support(enum pai_types t)
|
||||
{
|
||||
@@ -344,9 +689,40 @@ static void check_type_name(const char *type)
|
||||
errx(EXIT_FAILURE, "Invalid argument for -t %s", type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get list of specified CPUs from command line. Check if these CPUs
|
||||
* exist and are online. Ignore those CPUs which are not available and
|
||||
* issue one warning when CPUs have been specified but are not online.
|
||||
*/
|
||||
static void get_cpulist(char *parm)
|
||||
{
|
||||
bool warned = false;
|
||||
cpu_set_t cpulist;
|
||||
int i, rc;
|
||||
|
||||
CPU_ZERO(&cpulist);
|
||||
rc = libcpumf_cpuset(parm, &cpulist);
|
||||
if (rc)
|
||||
err(EXIT_FAILURE, "Cannot parse cpulist %s", parm);
|
||||
for (i = 0; i < CPU_SETSIZE; ++i) {
|
||||
if (CPU_ISSET(i, &cpulist) && !CPU_ISSET(i, &cpu_online_mask)) {
|
||||
if (!warned) {
|
||||
warnx("some CPU(s) are offline, ignored");
|
||||
warned = true;
|
||||
}
|
||||
}
|
||||
if (!CPU_ISSET(i, &cpulist) && CPU_ISSET(i, &cpu_online_mask))
|
||||
CPU_CLR(i, &cpu_online_mask);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
bool list_only = true;
|
||||
enum util_fmt_t fmt;
|
||||
bool i_flag = false;
|
||||
bool l_flag = false;
|
||||
char *endchar;
|
||||
int ch;
|
||||
|
||||
util_list_init(&pai_list, struct pai_node, node);
|
||||
@@ -358,6 +734,15 @@ int main(int argc, char **argv)
|
||||
default:
|
||||
util_opt_print_parse_error(ch, argv);
|
||||
return EXIT_FAILURE;
|
||||
case 'a':
|
||||
allcpu = true;
|
||||
list_only = false;
|
||||
break;
|
||||
case 'd':
|
||||
delta = true;
|
||||
firstread = true;
|
||||
list_only = false;
|
||||
break;
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
@@ -365,21 +750,80 @@ int main(int argc, char **argv)
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
return EXIT_SUCCESS;
|
||||
case 'i':
|
||||
i_flag = true;
|
||||
list_only = false;
|
||||
errno = 0;
|
||||
read_interval = strtoul(optarg, &endchar, 0);
|
||||
if (errno || *endchar)
|
||||
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||||
break;
|
||||
case 'l':
|
||||
l_flag = true;
|
||||
list_only = false;
|
||||
errno = 0;
|
||||
loops = strtoul(optarg, &endchar, 0);
|
||||
if (errno || *endchar)
|
||||
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||||
break;
|
||||
case 'n':
|
||||
numsort = true;
|
||||
break;
|
||||
case 's':
|
||||
list_only = false;
|
||||
shortname = true;
|
||||
break;
|
||||
case 't':
|
||||
check_type_name(optarg);
|
||||
break;
|
||||
case 'x':
|
||||
list_only = false;
|
||||
ctrformat = "%lx";
|
||||
break;
|
||||
case 'X':
|
||||
list_only = false;
|
||||
ctrformat = "%#lx";
|
||||
break;
|
||||
case OPT_FORMAT:
|
||||
if (!util_fmt_name_to_type(optarg, &fmt))
|
||||
errx(EXIT_FAILURE, "Supported formats:" FMT_TYPE_NAMES);
|
||||
output_format = fmt;
|
||||
break;
|
||||
case 'c':
|
||||
ctrlist = util_strdup(optarg);
|
||||
util_str_rm_whitespace(optarg, ctrlist);
|
||||
util_str_toupper(ctrlist);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Nothing specified, show all PAI counters */
|
||||
if (i_flag && !l_flag) {
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read currently online CPUs and create a bit mask.
|
||||
* This bitmap of online CPUs is used to check command line parameter
|
||||
* for valid CPUs
|
||||
* When any of the flags which set variable list_only to false have
|
||||
* be specified, lets also show the counter value, not just list them.
|
||||
*/
|
||||
if (optind < argc) /* List of CPUs on command line */
|
||||
list_only = false;
|
||||
if (!list_only) { /* Show counter values */
|
||||
ch = libcpumf_cpuset_fn(S390_CPUS_ONLINE, &cpu_online_mask);
|
||||
if (ch)
|
||||
err(EXIT_FAILURE, "Cannot read file /sys/" S390_CPUS_ONLINE);
|
||||
while (optind < argc)
|
||||
get_cpulist(argv[optind++]);
|
||||
max_cpus = CPU_COUNT(&cpu_online_mask);
|
||||
if (!loops)
|
||||
loops = 1;
|
||||
}
|
||||
|
||||
/* Nothing specified, use all PAI counters */
|
||||
if (!pai_types_show)
|
||||
pai_types_show = (1 << pai_type_crypto) | (1 << pai_type_nnpa);
|
||||
|
||||
@@ -393,7 +837,12 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
sort_painode();
|
||||
show_painode();
|
||||
ch = EXIT_SUCCESS;
|
||||
if (!list_only)
|
||||
event_painode();
|
||||
else
|
||||
list_painode();
|
||||
free_painode();
|
||||
free(ctrlist);
|
||||
return ch;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ chcpumf \- manage the CPU-measurement facilities support
|
||||
.SH SYNOPSIS
|
||||
.B chcpumf
|
||||
.RB [ \-m | \-\-min
|
||||
.IR num_sdb ]
|
||||
.IR NUM_SDB ]
|
||||
.RB [ \-x | \-\-max
|
||||
.IR num_sdb ]
|
||||
.IR NUM_SDB ]
|
||||
.RB [ \-V | \-\-verbose ]
|
||||
.br
|
||||
.B chcpumf
|
||||
@@ -36,14 +36,14 @@ facilities for Linux on System z.
|
||||
.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-m ", " \-\-min " \fInum_sdb\fP"
|
||||
.BR \-m ", " \-\-min " \fINUM_SDB\fP"
|
||||
Specifies the minimum sampling facility buffer size in sample-data-blocks
|
||||
(SDB). A sample-data-block consumes about 4 kilobytes.
|
||||
|
||||
This is the initial buffer size when you start the sampling facility.
|
||||
.
|
||||
.TP
|
||||
.BR \-x ", " \-\-max " \fInum_sdb\fP"
|
||||
.BR \-x ", " \-\-max " \fINUM_SDB\fP"
|
||||
Specifies the maximum sampling facility buffer size in sample-data-blocks
|
||||
(SDB). A sample-data-block consumes about 4 kilobytes.
|
||||
|
||||
|
||||
@@ -16,14 +16,16 @@ lshwc \- extract CPU Measurement Facilities counter sets
|
||||
\*c
|
||||
.RB [ \-a ][ \-d ][ \-H ][ \-s ][ \-x ][ \-X ][ \-q ]
|
||||
.RB [ \-l
|
||||
.IR count ]
|
||||
.IR COUNT ]
|
||||
.RB [ \-i
|
||||
.IR interval ]
|
||||
.IR INTERVAL ]
|
||||
.RB [ \-t
|
||||
.IR time ]
|
||||
.IR TIME ]
|
||||
.RB [ \-f
|
||||
.IR format ]
|
||||
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
||||
.IR FORMAT ]
|
||||
.RB [ \-c
|
||||
.IR LIST ]
|
||||
\fR[\fICPULIST\fR][:\fISETS\fR]\fP
|
||||
.br
|
||||
\*c
|
||||
.BR \-h | \-\-help
|
||||
@@ -62,13 +64,13 @@ Without this flag the total value of each counter is displayed.
|
||||
See Examples.
|
||||
.
|
||||
.TP
|
||||
.BR \-i ", " \-\-interval \fI\ seconds\fP
|
||||
.BR \-i ", " \-\-interval \fI\ SECONDS\fP
|
||||
Specifies a time interval, in seconds,
|
||||
that the command waits between read operations.
|
||||
The default is 60 seconds.
|
||||
.
|
||||
.TP
|
||||
.BR \-l ", " \-\-loop \fI\ count\fP
|
||||
.BR \-l ", " \-\-loop \fI\ COUNT\fP
|
||||
Performs the specified number of read operations.
|
||||
.
|
||||
.TP
|
||||
@@ -134,7 +136,7 @@ Displays counter values as hexadecimal values with a leading 0x prefix.
|
||||
Apply quoting to every output element, regardless of content or format.
|
||||
.
|
||||
.TP
|
||||
.BR \-f ", " \-\-format \fI\ format\fP
|
||||
.BR \-f ", " \-\-format \fI\ FORMAT\fP
|
||||
Retrieve output in one of the following formats:
|
||||
JSON, CSV, JSON-SEQ or PAIRS.
|
||||
If no format is specified, the output defaults to CSV.
|
||||
@@ -173,7 +175,12 @@ and \fBvalue\fP.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
||||
.BR \-c ", " \-\-counters \fI\ LIST\fP
|
||||
Specify a comma-separated list of counter names to be printed.
|
||||
If a counter name does not match any valid counter name, it is omitted. (Implies -H)
|
||||
.
|
||||
.TP
|
||||
\fR[\fICPULIST\fR][:\fISETS\fR]\fP
|
||||
A comma-separated list of CPUs.
|
||||
Each CPU can optionally be followed by characters that specify the counter set.
|
||||
See below for details.
|
||||
@@ -206,7 +213,7 @@ Two read operations are performed and a summary line is printed for each
|
||||
read operation.
|
||||
.sp 1
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
# lshwc -l2 0-1:BP
|
||||
Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES(3),L1D_DIR_WRITES(4),
|
||||
L1D_PENALTY_CYCLES(5),PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
||||
@@ -220,7 +227,7 @@ Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES
|
||||
This example shows the counter values of the problem state counter set per CPU.
|
||||
CPU 0 and CPU 1 are selected.
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
.sp 1
|
||||
# lshwc -l3 -a 0-1:P
|
||||
Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
||||
@@ -239,7 +246,7 @@ Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
||||
This example shows the counter values of the basic counter set
|
||||
using delta output format.
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
.sp 1
|
||||
# lshwc -d -l 10 -i 5 -s :b
|
||||
Date,Time,CPU,B0,B1,B2,B3,B4,B5
|
||||
@@ -259,7 +266,7 @@ Date,Time,CPU,B0,B1,B2,B3,B4,B5
|
||||
This example shows the counter values of the problem state counter set with
|
||||
CPU 3 selected.
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
.sp 1
|
||||
# lshwc -l2 -a 3:P -H --format json
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
.TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs"
|
||||
.
|
||||
.SH NAME
|
||||
lspai \- list Processor Activity Instrumentation (PAI) counters
|
||||
\*c \- list Processor Activity Instrumentation (PAI) counters and values
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
\*c
|
||||
@@ -18,7 +18,22 @@ lspai \- list Processor Activity Instrumentation (PAI) counters
|
||||
.IR FORMAT ]
|
||||
.RB [ \-n ]
|
||||
.RB [ \-t
|
||||
.IR TYPE ]
|
||||
.br
|
||||
\*c
|
||||
.RB [ \-\-format
|
||||
.IR FORMAT ]
|
||||
.RB [ \-n ]
|
||||
.RB [ \-t
|
||||
.IR "\ TYPE" ]
|
||||
.RB [ \-a ][ \-d ][ \-i
|
||||
.IR SECONDS ]
|
||||
.RB [ \-l
|
||||
.IR COUNT ]
|
||||
.RB [ \-s ][ \-x ][ \-x ]
|
||||
.RB [ \-c
|
||||
.IR LIST ]
|
||||
.RI [ CPULIST ]
|
||||
.br
|
||||
\*c
|
||||
.BR \-h | \-\-help
|
||||
@@ -28,10 +43,34 @@ lspai \- list Processor Activity Instrumentation (PAI) counters
|
||||
.
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
On the first line
|
||||
\*c displays the Processor Activity Instrumentation (PAI) counters
|
||||
for Linux on IBM Z.
|
||||
The output is a human-readable list of available PAI counter
|
||||
names and numbers.
|
||||
The output lists the available PAI counter names
|
||||
and numbers in various formats.
|
||||
If
|
||||
.I CPULIST
|
||||
is specified or any of the options
|
||||
.BR \-a ,
|
||||
.BR \-d ,
|
||||
.BR \-s ,
|
||||
.BR \-x ,
|
||||
.BR \-X ,
|
||||
.B \-l
|
||||
or
|
||||
.BR \-i,
|
||||
are present on the command line,
|
||||
\*c command extracts counter values from the
|
||||
specified PAI counter sets.
|
||||
Counter sets can be specified and extracted for individual CPUs.
|
||||
By default the output is a comma-separated list of values.
|
||||
Each line starts with a timestamp
|
||||
followed by the extracted counter values.
|
||||
A header line shows each counter name and the
|
||||
counter number in parenthesis.
|
||||
For other formats, use the
|
||||
.B \-\-format
|
||||
option.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-h ", " \-\-help
|
||||
@@ -42,7 +81,44 @@ Displays help information, then exits.
|
||||
Displays version information, then exits.
|
||||
.
|
||||
.TP
|
||||
.BR \-t ", " \-\-type "\ TYPE"
|
||||
.BR \-a ", " \-\-allcpu
|
||||
Displays counter values from each CPU.
|
||||
The default is a total summary line of all counters from all CPUs.
|
||||
.
|
||||
.TP
|
||||
.BR \-d ", " \-\-delta
|
||||
Displays counter values in form of deltas.
|
||||
Each counter value shows the increment to the previous output line.
|
||||
Without this flag the total value of each counter is displayed.
|
||||
.
|
||||
.TP
|
||||
.BR \-c ", " \-\-counters \fI\ LIST\fP
|
||||
Specify a comma separated list of counter names to
|
||||
be printed.
|
||||
If a counter name does not match any valid counter
|
||||
name, it is omitted.
|
||||
.
|
||||
.TP
|
||||
.BR \-s ", " \-\-short
|
||||
Displays counter names as short names in the heading.
|
||||
A short name consists of
|
||||
a letter for the counter set followed by a number.
|
||||
For example,
|
||||
this flag displays counter number 0 in the PAI crypto counter
|
||||
set as
|
||||
.B C0
|
||||
instead of
|
||||
.BR CRYPTO_ALL .
|
||||
Valid counter set letters are:
|
||||
.RS
|
||||
.IP C
|
||||
PAI crypto counter set
|
||||
.IP N
|
||||
PAI NNPA counter set
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.BR \-t ", " \-\-type "\ \fITYPE\fP"
|
||||
Specifies the PAI counter set to list.
|
||||
Valid counter set values are
|
||||
.I crypto
|
||||
@@ -63,6 +139,26 @@ Retrieve output in one of the following formats:
|
||||
JSON, csv, json-seq or pairs.
|
||||
If no format is specified,
|
||||
the output defaults to a human-readable format.
|
||||
.
|
||||
.TP
|
||||
.BR \-i ", " \-\-interval \fI\ SECONDS\fP
|
||||
Specifies a time interval, in seconds,
|
||||
that the command waits between read operations.
|
||||
The default is 60 seconds.
|
||||
.
|
||||
.TP
|
||||
.BR \-l ", " \-\-loop \fI\ COUNT\fP
|
||||
Performs the specified number of read operations.
|
||||
The default is one.
|
||||
.
|
||||
.TP
|
||||
.BR \-x ", " \-\-hex
|
||||
Displays counter values as hexadecimal values.
|
||||
.
|
||||
.TP
|
||||
.BR \-X ", " \-\-hex0x
|
||||
Displays counter values as hexadecimal values with a leading 0x prefix.
|
||||
.
|
||||
.SH "EXAMPLE"
|
||||
The \*c invocation lists all PAI Neural Network Processing Assist Facility
|
||||
(NNPA) counters in numeric order:
|
||||
@@ -83,6 +179,58 @@ suitable for
|
||||
.IR perf "(8)"
|
||||
event specification by name.
|
||||
The third gives a short explanation, if available.
|
||||
.sp 1
|
||||
The \*c invocation displays all PAI Neural Network Processing Assist Facility
|
||||
(NNPA) counters values.
|
||||
Ten iterations with a two second delay in between
|
||||
is printed on the screen.
|
||||
.nf
|
||||
# lspai -i 2 -l 10 -t nnpa -n
|
||||
Date,Time,CPU,NNPA_ALL(0),NNPA_ADD(1),NNPA_SUB(2),NNPA_MUL(3),NNPA_DIV(4),
|
||||
NNPA_MIN(5),NNPA_MAX(6),NNPA_LOG(7),NNPA_EXP(8),NNPA_IBM_RESERVED_9(9),
|
||||
NNPA_RELU(10),NNPA_TANH(11),NNPA_SIGMOID(12),NNPA_SOFTMAX(13),NNPA_BATCHNORM(14),
|
||||
NNPA_MAXPOOL2D(15),NNPA_AVGPOOL2D(16),NNPA_LSTMACT(17),NNPA_GRUACT(18),
|
||||
NNPA_CONVOLUTION(19),NNPA_MATMUL_OP(20),NNPA_MATMUL_OP_BCAST23(21),
|
||||
NNPA_SMALLBATCH(22),NNPA_LARGEDIM(23),NNPA_SMALLTENSOR(24),NNPA_1MFRAME(25),
|
||||
NNPA_2GFRAME(26),NNPA_ACCESSEXCEPT(27)
|
||||
2025-05-13,08:18:27,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
2025-05-13,08:18:29,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
2025-05-13,08:18:31,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
2025-05-13,08:18:33,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
2025-05-13,08:18:35,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
2025-05-13,08:18:37,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
2025-05-13,08:18:39,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
2025-05-13,08:18:41,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
2025-05-13,08:18:43,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
2025-05-13,08:18:45,Total,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
#
|
||||
.fi
|
||||
.sp 1
|
||||
This \*c example displays the value of PAI crypto counter number 7.
|
||||
It is field number 116 in the output.
|
||||
This counter is incremented after \*c is started and
|
||||
the table shows the delta values for PAI crypto counter 7.
|
||||
.nf
|
||||
# lspai -d -s -i 5 -l3 -t crypto | \
|
||||
awk -F , '{ print $1 "," $2 "," $3 "," $4 "," $116; }'
|
||||
Date,Time,CPU,C0,C7
|
||||
2025-06-24,13:02:24,Total,0,0
|
||||
2025-06-24,13:02:29,Delta,77122286,77122284
|
||||
2025-06-24,13:02:34,Delta,92574403,92574403
|
||||
#
|
||||
.fi
|
||||
.sp 1
|
||||
The following table shows the total value for PAI crypto counter 7
|
||||
after \*c is started.
|
||||
.nf
|
||||
# lspai -s -i 5 -l3 -t crypto | \
|
||||
awk -F , '{ print $1 "," $2 "," $3 "," $4 "," $116; }'
|
||||
Date,Time,CPU,C0,C7
|
||||
2025-06-24,13:03:01,Total,0,0
|
||||
2025-06-24,13:03:06,Total,72550754,72566121
|
||||
2025-06-24,13:03:11,Total,164633982,164650377
|
||||
#
|
||||
.fi
|
||||
.SH "SEE ALSO"
|
||||
.BR pai (8)
|
||||
.BR lscpumf (8)
|
||||
|
||||
@@ -15,24 +15,24 @@ pai \- record and report Processor Activity Instrumentation (PAI) counters
|
||||
.SH SYNOPSIS
|
||||
\*c
|
||||
.RB [ \-V ][ \-m | \-\-mapsize
|
||||
.IR size ]
|
||||
.IR SIZE ]
|
||||
.RB [ \-i | \-\-interval
|
||||
.IR ms ]
|
||||
.IR MS ]
|
||||
.RB [ \-R | \-\-realtime
|
||||
.IR prio ]
|
||||
.BR \-c | \-\-crypto [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ]
|
||||
.IR PRIO ]
|
||||
.BR \-c | \-\-crypto [ \fICPULIST ][: \fIDATA\fR "] [" \fILOOPS\fP ]
|
||||
.br
|
||||
\*c
|
||||
.RB [ \-V ][ \-m | \-\-mapsize
|
||||
.IR size ]
|
||||
.IR SIZE ]
|
||||
.RB [ \-i | \-\-interval
|
||||
.IR ms ]
|
||||
.IR MS ]
|
||||
.RB [ \-R | \-\-realtime
|
||||
.IR prio ]
|
||||
.BR \-n | \-\-nnpa [ \fIcpulist ][: \fIdata\fR "] [" \fIloops\fP ]
|
||||
.IR PRIO ]
|
||||
.BR \-n | \-\-nnpa [ \fICPULIST ][: \fIDATA\fR "] [" \fILOOPS\fP ]
|
||||
.br
|
||||
\*c
|
||||
.RB [ \-V ][ \-H | \-\-humantime ][ \-S | \-\-summary "] " \-r | \-\-report " [" \fIfiles\fP ]
|
||||
.RB [ \-V ][ \-H | \-\-humantime ][ \-S | \-\-summary "] " \-r | \-\-report " [" \fIFILES\fP ]
|
||||
.br
|
||||
\*c
|
||||
.BR \-h | \-\-help
|
||||
@@ -73,7 +73,7 @@ Reporting evaluates files that are created by recording.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-c ", " \-\-crypto "\fR[\fIcpulist\fR][:\fIdata\fR]"
|
||||
.BR \-c ", " \-\-crypto "\fR[\fICPULIST\fR][:\fIDATA\fR]"
|
||||
Records data for all (default) or a specified list of CPUs.
|
||||
The CPU list is a comma-separated list of CPU numbers and ranges.
|
||||
In a range, a hyphen separates the first CPU number
|
||||
@@ -106,7 +106,7 @@ execution.
|
||||
.RE
|
||||
.
|
||||
.TP
|
||||
.BR \-n ", " \-\-nnpa "\fR[\fIcpulist\fR][:\fIdata\fR]"
|
||||
.BR \-n ", " \-\-nnpa "\fR[\fICPULIST\fR][:\fIDATA\fR]"
|
||||
Records data for all (default) or a specified list of CPUs.
|
||||
The CPU list is a comma-separated list of CPU numbers and ranges.
|
||||
In a range, a hyphen separates the first CPU number
|
||||
@@ -241,7 +241,7 @@ The program runs for 10 seconds
|
||||
(10 intervals of 1000 milliseconds).
|
||||
.sp 1
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
# \*c -c0 10
|
||||
.ft R
|
||||
.fi
|
||||
@@ -256,7 +256,7 @@ and a comma-separated list of counter number, colon (:),
|
||||
and the counter value n hexadecimal notation.
|
||||
.sp 1
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
# \*c -r
|
||||
0x62a668f2fa 0 event 4096 sample pid 4956/4956 9:0xa7,73:0x8,74:0x18
|
||||
0x6319c75653 0 event 4096 sample pid 4972/4972 32:0x1
|
||||
|
||||
10
cpumf/pai.c
10
cpumf/pai.c
@@ -59,14 +59,6 @@ static struct util_list list_pai_event;
|
||||
static struct util_list list_pmu_event;
|
||||
static bool summary;
|
||||
|
||||
/* System call to perf_event_open(2) */
|
||||
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
|
||||
int cpu, int group_fd, unsigned long flags)
|
||||
{
|
||||
return syscall(__NR_perf_event_open, hw_event, pid, cpu,
|
||||
group_fd, flags);
|
||||
}
|
||||
|
||||
static void ev_dealloc(void)
|
||||
{
|
||||
struct pai_event *next, *p;
|
||||
@@ -743,8 +735,8 @@ static int add_events(int type)
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
free(pmuname);
|
||||
warnx("failed building event list for %s", pmuname);
|
||||
free(pmuname);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/dasd_base.h"
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#define RD_BUFFER_SIZE 80
|
||||
@@ -283,7 +283,7 @@ static int dinfo_create_devnode(dev_t dev, char **devno)
|
||||
char filename[] = "dasdinfo0000";
|
||||
mode_t mode;
|
||||
unsigned int path;
|
||||
int retry;
|
||||
unsigned int retry;
|
||||
int rc;
|
||||
int fd;
|
||||
|
||||
@@ -294,7 +294,7 @@ static int dinfo_create_devnode(dev_t dev, char **devno)
|
||||
if (pathname[path] == NULL)
|
||||
continue;
|
||||
for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
|
||||
sprintf(filename, "dasdinfo%04d", retry);
|
||||
snprintf(filename, sizeof(filename), "dasdinfo%04d", retry);
|
||||
result = dinfo_make_path(pathname[path], filename);
|
||||
if (result == NULL)
|
||||
return -1;
|
||||
@@ -354,18 +354,26 @@ static int dinfo_extract_dev(dev_t *dev, char *str)
|
||||
static int dinfo_get_dev_from_blockdev(char *blockdev, dev_t *dev)
|
||||
{
|
||||
char *readbuf = NULL;
|
||||
char *path;
|
||||
|
||||
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
||||
if (!readbuf) {
|
||||
warnx("Error: Not enough memory to allocate readbuffer");
|
||||
return -1;
|
||||
}
|
||||
if (util_file_read_line(readbuf, RD_BUFFER_SIZE,
|
||||
"/sys/block/%s/dev", blockdev) < 0)
|
||||
path = util_path_sysfs("block/%s/dev", blockdev);
|
||||
if (util_file_read_line(readbuf, RD_BUFFER_SIZE, path) < 0) {
|
||||
free(path);
|
||||
free(readbuf);
|
||||
return -1;
|
||||
if (dinfo_extract_dev(dev, readbuf) != 0)
|
||||
}
|
||||
free(path);
|
||||
if (dinfo_extract_dev(dev, readbuf) != 0) {
|
||||
free(readbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(readbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -373,8 +381,9 @@ static int
|
||||
dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
|
||||
int tflag, struct FTW *ftwbuf)
|
||||
{
|
||||
enum { LINK_DIR_SIZE = 128 };
|
||||
char linkdir[LINK_DIR_SIZE];
|
||||
char *tempdir;
|
||||
char linkdir[128];
|
||||
ssize_t i;
|
||||
|
||||
if (tflag != FTW_D || (strncmp((fpath + ftwbuf->base), searchbusid,
|
||||
@@ -387,12 +396,12 @@ dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
|
||||
*/
|
||||
if (asprintf(&tempdir, "%s/driver", fpath) < 0)
|
||||
return -1;
|
||||
i = readlink(tempdir, linkdir, 128);
|
||||
i = readlink(tempdir, linkdir, LINK_DIR_SIZE);
|
||||
free(tempdir);
|
||||
if ((i < 0) || (i >= 128))
|
||||
if (i < 0 || i >= LINK_DIR_SIZE)
|
||||
return -1;
|
||||
/* append '\0' because readlink returns non zero terminated string */
|
||||
tempdir[i + 1] = '\0';
|
||||
linkdir[i] = '\0';
|
||||
if (strstr(linkdir, "dasd") == NULL)
|
||||
return FTW_CONTINUE;
|
||||
free(busiddir);
|
||||
@@ -439,7 +448,7 @@ dinfo_get_blockdev_from_busid(char *busid, char **blkdev)
|
||||
|
||||
char *tempdir = NULL;
|
||||
char *result = NULL;
|
||||
char *sysfsdir = "/sys/devices/";
|
||||
char *sysfsdir = util_path_sysfs("devices/");
|
||||
|
||||
/* dinfo_is_devnode needs to know the busid */
|
||||
searchbusid = busid;
|
||||
@@ -475,6 +484,7 @@ out:
|
||||
out2:
|
||||
free(busiddir);
|
||||
free(result);
|
||||
free(sysfsdir);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -486,6 +496,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
||||
DIR *directory = NULL;
|
||||
struct dirent *dir_entry = NULL;
|
||||
int rc = 0;
|
||||
char *path;
|
||||
|
||||
if (stat(devnode, &stat_buffer) != 0) {
|
||||
warnx("Error: could not stat %s", devnode);
|
||||
@@ -495,32 +506,34 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
||||
sprintf(stat_dev, "%d:%d", major(stat_buffer.st_rdev),
|
||||
minor(stat_buffer.st_rdev));
|
||||
|
||||
directory = opendir("/sys/block/");
|
||||
path = util_path_sysfs("block/");
|
||||
directory = opendir(path);
|
||||
if (directory == NULL) {
|
||||
warnx("Error: could not open directory /sys/block");
|
||||
warnx("Error: could not open directory %s", path);
|
||||
free(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
||||
if (!readbuf) {
|
||||
warnx("Error: Not enough memory to allocate readbuffer");
|
||||
free(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((dir_entry = readdir(directory)) != NULL) {
|
||||
if (util_file_read_line(readbuf, RD_BUFFER_SIZE,
|
||||
"/sys/block/%s/dev",
|
||||
if (util_file_read_line(readbuf, RD_BUFFER_SIZE, "%s%s/dev", path,
|
||||
dir_entry->d_name) < 0)
|
||||
continue;
|
||||
|
||||
if (strncmp(stat_dev, readbuf,
|
||||
MAX(strlen(stat_dev), strlen(readbuf) - 1)) == 0) {
|
||||
rc = snprintf(*uidfile, RD_BUFFER_SIZE,
|
||||
"/sys/block/%s/device/uid",
|
||||
rc = snprintf(*uidfile, RD_BUFFER_SIZE, "%s%s/device/uid", path,
|
||||
dir_entry->d_name);
|
||||
if (rc >= RD_BUFFER_SIZE) {
|
||||
fprintf(stderr,
|
||||
"Error: Device name was truncated\n");
|
||||
free(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -529,6 +542,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
||||
}
|
||||
|
||||
closedir(directory);
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -626,16 +640,18 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
||||
uidfile = dinfo_malloc(RD_BUFFER_SIZE);
|
||||
if (!(readbuf && uidfile))
|
||||
if (!readbuf)
|
||||
exit(1);
|
||||
|
||||
/* try to read the uid attribute */
|
||||
if (busid) {
|
||||
sprintf(uidfile, "/sys/bus/ccw/devices/%s/uid", busid);
|
||||
uidfile = util_path_sysfs("bus/ccw/devices/%s/uid", busid);
|
||||
} else if (blockdev) {
|
||||
sprintf(uidfile, "/sys/block/%s/device/uid", blockdev);
|
||||
uidfile = util_path_sysfs("block/%s/device/uid", blockdev);
|
||||
} else if (devnode) {
|
||||
uidfile = dinfo_malloc(RD_BUFFER_SIZE);
|
||||
if (!uidfile)
|
||||
exit(1);
|
||||
if (dinfo_get_uid_from_devnode(&uidfile, devnode) != 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
. ds pfont \\n[.f]
|
||||
. nh
|
||||
. na
|
||||
. ft CW
|
||||
. ft CR
|
||||
\\$*
|
||||
. ft \\*[pfont]
|
||||
. ad
|
||||
|
||||
@@ -18,3 +18,17 @@ CPI_SYSTEM_NAME=""
|
||||
# CPI sysplex name
|
||||
#
|
||||
CPI_SYSPLEX_NAME=""
|
||||
|
||||
#
|
||||
# CPI permit on protected virtualization guests
|
||||
#
|
||||
# Important: Set CPI_PERMIT_ON_PVGUEST=1 only if you trust the host system.
|
||||
# Enabling these options allows the host to receive potentially sensitive
|
||||
# Control-Program Identification (CPI) data from the protected virtualization
|
||||
# guest, including:
|
||||
# - system_type
|
||||
# - system_level
|
||||
# - sysplex_name
|
||||
# - system_name
|
||||
#
|
||||
CPI_PERMIT_ON_PVGUEST=
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <linux/perf_event.h>
|
||||
|
||||
#define S390_CPUMF_CF "devices/cpum_cf/"
|
||||
#define S390_CPUMF_CFDIAG "devices/cpum_cf_diag/"
|
||||
@@ -115,7 +117,7 @@ bool libcpumf_have_cpumcf(void);
|
||||
*
|
||||
* @param[out] min Minimum supported sampling interval
|
||||
* @param[out] max Maximum supported sampling interval
|
||||
* @param[out] speed Current CPU speed, number of CPU cylces per
|
||||
* @param[out] speed Current CPU speed, number of CPU cycles per
|
||||
* microsecond
|
||||
* @param[out] basic_sz Basic sample size in bytes
|
||||
* @param[out] diag_sz Diagnostic sample size in bytes
|
||||
@@ -182,4 +184,37 @@ bool libcpumf_have_pai_ext(void);
|
||||
* @retval false PAI_NNPA counter Facility is not available
|
||||
*/
|
||||
bool libcpumf_have_pai_nnpa(void);
|
||||
|
||||
/**
|
||||
* Wrapper for the perf_event_open syscall used to configure performance events.
|
||||
* This function simplifies usage of perf_event_open and provides a consistent
|
||||
* interface for libcpumf internals.
|
||||
*
|
||||
* @param hw_event Pointer to perf_event_attr structure describing the event
|
||||
* @param pid Target process ID (0 for current process)
|
||||
* @param cpu Target CPU (-1 for all CPUs)
|
||||
* @param group_fd File descriptor of event group leader, or -1 if none
|
||||
* @param flags Additional flags (usually 0)
|
||||
*
|
||||
* @return File descriptor for the opened event on success
|
||||
* @return -1 on failure, errno is set appropriately
|
||||
*/
|
||||
|
||||
long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd,
|
||||
unsigned long flags);
|
||||
|
||||
/**
|
||||
* Check if a counter name is present in a comma-separated list of counters.
|
||||
*
|
||||
* This function checks whether the given counter name appears in the
|
||||
* provided list. If the list is NULL, the function assumes all counters
|
||||
* are allowed and returns true.
|
||||
*
|
||||
* @param name Name of the counter to search for
|
||||
* @param ctrlist Comma-separated list of counter names, or NULL
|
||||
*
|
||||
* @return true if the counter is in the list or list is NULL
|
||||
* @return false otherwise
|
||||
*/
|
||||
bool ctr_in_list(char *name, char *ctrlist);
|
||||
#endif
|
||||
|
||||
17
include/lib/util_str.h
Normal file
17
include/lib/util_str.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @defgroup util_str_h util_str: String functions
|
||||
* @{
|
||||
* @brief Manipulate and work with strings
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef LIB_UTIL_STR_H
|
||||
#define LIB_UTIL_STR_H
|
||||
|
||||
void util_str_rm_whitespace(const char *src, char *dest);
|
||||
|
||||
#endif /** LIB_UTIL_STR_H @} */
|
||||
@@ -15,6 +15,7 @@ install-man: $(MANS)
|
||||
msection=`echo $$man |sed 's/.*\.\([1-9]\)$$/man\1/'` ; \
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 -D $$man $(DESTDIR)$(MANDIR)/$$msection/$$man ; \
|
||||
done
|
||||
ln -f -s chiucvallow.8 $(DESTDIR)$(MANDIR)/man8/lsiucvallow.8
|
||||
|
||||
clean:
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ terminal application
|
||||
To connect to the first z/VM IUCV HVC terminal device on the z/VM guest virtual
|
||||
machine "LNX1234", run:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
iucvconn LNX1234 lnxhvc0
|
||||
@@ -215,7 +215,7 @@ For each HVC terminal device, a \fBgetty\fP program must be started.
|
||||
To configure and start \fBgetty\fP on a HVC terminal, open
|
||||
.BR /etc/inittab (5),
|
||||
and add a new entry similar to this one:
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
|
||||
@@ -261,7 +261,7 @@ IUCV HVC terminal:
|
||||
.IP "1." 4
|
||||
To display the current terminal name, issue the command:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
user@host:~$ echo $TERM
|
||||
@@ -275,7 +275,7 @@ To connect to a z/VM IUCV HVC terminal, run
|
||||
.BR iucvconn (1)
|
||||
and log in as usual:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
user@host:~$ iucvconn MYLNX01 lnxhvc0
|
||||
@@ -289,7 +289,7 @@ user@MYLNX01:~$
|
||||
To assign the terminal name from step 1. to the terminal environment variable,
|
||||
issue the following command:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
user@MYLNX01:~$ export TERM=xterm
|
||||
@@ -304,7 +304,7 @@ environment variable.
|
||||
.PP
|
||||
For getty programs, a sample terminal environment configuration might look like:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
h0:2345:respawn:/sbin/agetty -L 9600 hvc0 linux
|
||||
|
||||
@@ -123,7 +123,7 @@ the characters to the connected terminal.
|
||||
To access the "lnxterm" terminal on the Linux instance in
|
||||
z/VM guest virtual machine LNXSYS01:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
iucvconn LNXSYS01 lnxterm
|
||||
@@ -134,7 +134,7 @@ iucvconn LNXSYS01 lnxterm
|
||||
To access the first z/VM IUCV HVC terminal on the Linux instance in
|
||||
z/VM guest virtual machine LNXSYS02:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
iucvconn LNXSYS02 lnxhvc0
|
||||
@@ -145,7 +145,7 @@ iucvconn LNXSYS02 lnxhvc0
|
||||
To create a transcript of the terminal session to the Linux instance in
|
||||
z/VM guest virtual machine LNXSYS99:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
iucvconn -s ~/transcripts/lnxsys99 LNXSYS99 lnxhvc0
|
||||
|
||||
@@ -94,7 +94,7 @@ additional options.
|
||||
.SH EXAMPLES
|
||||
To allow remote logins using the terminal identifier "lnxterm":
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
iucvtty lnxterm
|
||||
@@ -104,7 +104,7 @@ iucvtty lnxterm
|
||||
|
||||
To only allow users from LNXSYS01 to connect to terminal "lnxterm":
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
iucvtty -a LNXSYS01 lnxterm
|
||||
@@ -115,7 +115,7 @@ iucvtty -a LNXSYS01 lnxterm
|
||||
To only allow users from LNXSYS10 through LNXSYS19 to connect to terminal
|
||||
"lnxterm":
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
iucvtty -a "LNXSYS1[0-9]" lnxterm
|
||||
@@ -125,7 +125,7 @@ iucvtty -a "LNXSYS1[0-9]" lnxterm
|
||||
|
||||
To use \fB/sbin/sulogin\fP instead of \fB/bin/login\fP for terminal "suterm":
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
iucvtty suterm -- /sbin/sulogin
|
||||
@@ -136,7 +136,7 @@ iucvtty suterm -- /sbin/sulogin
|
||||
An entry in \fB/etc/inittab\fP to facilitate user logins on terminal "lnxterm"
|
||||
with \fB/bin/login\fP could be:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
t1:2345:respawn:/usr/bin/iucvtty lnxterm
|
||||
@@ -147,7 +147,7 @@ t1:2345:respawn:/usr/bin/iucvtty lnxterm
|
||||
An entry in \fB/etc/inittab\fP to facilitate user logins on terminal "suterm"
|
||||
with \fB/sbin/sulogin\fP in single user mode could be:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
s1:S:respawn:/usr/bin/iucvtty suterm -- /sbin/sulogin
|
||||
|
||||
@@ -79,7 +79,7 @@ users".
|
||||
|
||||
The output for "list" authorization is a list of z/VM guest virtual machines,
|
||||
for example:
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25i
|
||||
.nf
|
||||
|
||||
@@ -94,7 +94,7 @@ guest5
|
||||
.ft
|
||||
The output for "regex" authorization is a list of one or more
|
||||
regular expressions, for example:
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25i
|
||||
.nf
|
||||
|
||||
@@ -109,7 +109,7 @@ Regular expressions for your authorization:
|
||||
If \fBts-shell\fP is configured to connect to particular z/VM guest virtual
|
||||
machines only, the output for "regex" authorization is followed by a list of
|
||||
the user IDs that match at least one of the regular expressions:
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25i
|
||||
.nf
|
||||
|
||||
@@ -139,7 +139,7 @@ To change the default terminal identifier, use the \fBterminal\fP command.
|
||||
|
||||
In the following example, a user opens a terminal connection to the Linux
|
||||
instance in z/VM guest virtual machine LNXSYS01:
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25i
|
||||
.nf
|
||||
|
||||
@@ -164,7 +164,7 @@ If \fBterminal\fP is called with the \fIidentifier\fP being specified,
|
||||
If \fIidentifier\fP is not specified, the current default terminal identifier
|
||||
is displayed:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25i
|
||||
.nf
|
||||
user@ts-shell> terminal
|
||||
@@ -212,7 +212,7 @@ guest virtual machines.
|
||||
A typical \fBIUCV\fP authorization statement in the z/VM directory entry of the
|
||||
terminal server z/VM guest virtual machine might be:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
IUCV ANY
|
||||
@@ -301,7 +301,7 @@ instance.
|
||||
|
||||
For example, to create a list of all z/VM guest virtual machines with names that
|
||||
start with "LINUX" and are followed by digits, use:
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
|
||||
@@ -373,7 +373,7 @@ group name and prefixed with "@".
|
||||
|
||||
Here is an example of a Linux user and group authorization:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
alice = list:guest01,guest02
|
||||
@@ -395,7 +395,7 @@ name on a separate line.
|
||||
.PP
|
||||
The following example shows the usage of the \fIfile:\fP prefix:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
@testgrp = file:/etc/iucvterm/auth/test-systems.list
|
||||
@@ -427,7 +427,7 @@ To authorize user bob for all z/VM guest virtual machines with names that
|
||||
start with "lnx" and are followed with at least three but not more than five
|
||||
alphanumeric characters, use:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
bob = regex:lnx\\w{3,5}
|
||||
@@ -441,7 +441,7 @@ test or production environment: authorize all users in the "testgrp" group for
|
||||
all systems in the test environment; and respectively, authorize all users in
|
||||
the "prodgrp" group for all systems in the production environment:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
@testgrp = regex:test\\w+
|
||||
@@ -461,7 +461,7 @@ of the other type are ignored.
|
||||
|
||||
Example:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
@users = list:guest01,guest03,guest05
|
||||
@@ -484,7 +484,7 @@ To use the \fBts-shell\fP as the login shell for Linux users, follow these steps
|
||||
Add the path of the \fBts-shell\fP program to the \fI/etc/shells\fP file that
|
||||
contains the list of valid login shells:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
echo $(which ts-shell) >> /etc/shells
|
||||
@@ -498,7 +498,7 @@ Change the login shell of a particular Linux user using the
|
||||
.BR chsh (1)
|
||||
program:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
chsh -s $(which ts-shell) alice
|
||||
|
||||
@@ -116,7 +116,7 @@ specifies an exit status in this range.
|
||||
.SS inittab
|
||||
To start \fB/sbin/agetty\fP on terminal device "hvc1", specify:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
h1:2345:respawn:/sbin/\*s hvc1 /sbin/agetty -L 9600 %t linux
|
||||
@@ -128,7 +128,7 @@ h1:2345:respawn:/sbin/\*s hvc1 /sbin/agetty -L 9600 %t linux
|
||||
To start \fB/sbin/agetty\fP on terminal device "hvc1", add the following
|
||||
settings to the job file:
|
||||
.PP
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
respawn
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "lib/ccw.h"
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "lib/libcpumf.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_libc.h"
|
||||
|
||||
#define SERVICELEVEL "/proc/service_levels"
|
||||
|
||||
@@ -169,3 +170,31 @@ bool libcpumf_have_pai_nnpa(void)
|
||||
{
|
||||
return libcpumf_have_pai_sysfs(S390_SYSFS_PAI_NNPA);
|
||||
}
|
||||
|
||||
long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd,
|
||||
unsigned long flags)
|
||||
{
|
||||
return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
|
||||
}
|
||||
|
||||
bool ctr_in_list(char *name, char *ctrlist)
|
||||
{
|
||||
char *token;
|
||||
char *list;
|
||||
|
||||
if (!ctrlist) /* No --counters means all counters */
|
||||
return true;
|
||||
|
||||
list = util_strdup(ctrlist);
|
||||
token = strtok(list, ",");
|
||||
while (token) {
|
||||
if (strcmp(token, name) == 0) {
|
||||
free(list);
|
||||
return true;
|
||||
}
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
free(list);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5099,14 +5099,15 @@ int ekmf_generate_csr(const struct ekmf_config *config,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_ekmf_copy_pss_params(rsa_pss_params, &pss_params);
|
||||
if (rsa_pss_params != NULL)
|
||||
_ekmf_copy_pss_params(rsa_pss_params, &pss_params);
|
||||
|
||||
rc = SK_OPENSSL_generate_csr(key_blob, key_blob_size,
|
||||
subject_rdns, num_subject_rdns,
|
||||
subject_utf8, cert,
|
||||
extensions, num_extensions,
|
||||
digest_nid, &pss_params, &req,
|
||||
digest_nid, rsa_pss_params != NULL ?
|
||||
&pss_params : NULL, &req,
|
||||
&ext_lib_info.ext_lib, verbose);
|
||||
if (rc != 0) {
|
||||
pr_verbose(verbose, "SK_OPENSSL_generate_csr failed "
|
||||
@@ -5245,14 +5246,16 @@ int ekmf_generate_ss_cert(const struct ekmf_config *config,
|
||||
}
|
||||
}
|
||||
|
||||
_ekmf_copy_pss_params(rsa_pss_params, &pss_params);
|
||||
if (rsa_pss_params != NULL)
|
||||
_ekmf_copy_pss_params(rsa_pss_params, &pss_params);
|
||||
|
||||
rc = SK_OPENSSL_generate_ss_cert(key_blob, key_blob_size,
|
||||
subject_rdns, num_subject_rdns,
|
||||
subject_utf8, rcert,
|
||||
extensions, num_extensions,
|
||||
validity_days, digest_nid,
|
||||
&pss_params, &cert,
|
||||
rsa_pss_params != NULL ?
|
||||
&pss_params : NULL, &cert,
|
||||
&ext_lib_info.ext_lib, verbose);
|
||||
if (rc != 0) {
|
||||
pr_verbose(verbose, "SK_OPENSSL_generate_ss_cert failed "
|
||||
|
||||
33
libutil/util_str.c
Normal file
33
libutil/util_str.c
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Manipulate and work with strings
|
||||
*
|
||||
* Copyright IBM Corp. 2025
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "lib/util_str.h"
|
||||
|
||||
/**
|
||||
* Copy characters from \a src to \a dest, omitting all blank characters
|
||||
* (spaces and tabs). The resulting string in \a dest will be NUL terminated.
|
||||
*
|
||||
* @param[in] src Source string
|
||||
* @param[out] dest Destination buffer
|
||||
*/
|
||||
void util_str_rm_whitespace(const char *src, char *dest)
|
||||
{
|
||||
while (*src != '\0') {
|
||||
if (!isblank(*src)) {
|
||||
*dest = *src;
|
||||
dest++;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
*dest = '\0';
|
||||
}
|
||||
@@ -89,7 +89,7 @@ Socket communication with applications utilizing CMS AF_IUCV support
|
||||
An AF_IUCV socket is represented by the following format:
|
||||
.PP
|
||||
.RS 8
|
||||
.ft CW
|
||||
.ft CR
|
||||
.nf
|
||||
#define AF_IUCV 32
|
||||
|
||||
@@ -290,7 +290,7 @@ It must not match any z/VM user ID in your environment.
|
||||
To set an identifier, issue a command like this:
|
||||
.PP
|
||||
.RS 8
|
||||
.ft CW
|
||||
.ft CR
|
||||
echo \fIidentifier\fP > /sys/devices/qeth/\fI<bus-ID>\fP/hsuid
|
||||
.ft
|
||||
.RE
|
||||
@@ -303,7 +303,7 @@ For example, to use "MYHOST01" to bind AF_IUCV sockets to the
|
||||
HiperSockets device with bus-ID 0.0.8000, run:
|
||||
.PP
|
||||
.RS 8
|
||||
.ft CW
|
||||
.ft CR
|
||||
.nf
|
||||
echo "MYHOST01" > /sys/devices/qeth/0.0.8000/hsuid
|
||||
.fi
|
||||
@@ -366,7 +366,7 @@ z/VM guest virtual machine with a maximum of 10\^000 outstanding messages for ea
|
||||
incoming connection. Your z/VM guest virtual machine is permitted to connect to
|
||||
all other z/VM guest virtual machines. The total number of connections for your
|
||||
z/VM guest virtual machine cannot exceed 100.
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25i
|
||||
.nf
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
. ds pfont \fP
|
||||
. nh
|
||||
. na
|
||||
. ft CW
|
||||
. ft CR
|
||||
\\$*
|
||||
. ft \\*[pfont]
|
||||
. ad
|
||||
|
||||
53
rust/Cargo.lock
generated
53
rust/Cargo.lock
generated
@@ -47,7 +47,7 @@ version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -57,7 +57,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -205,9 +205,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "curl"
|
||||
version = "0.4.47"
|
||||
version = "0.4.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9fb4d13a1be2b58f14d60adba57c9834b78c62fd86c3e76a148f732686e9265"
|
||||
checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc"
|
||||
dependencies = [
|
||||
"curl-sys",
|
||||
"libc",
|
||||
@@ -215,14 +215,14 @@ dependencies = [
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"socket2",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curl-sys"
|
||||
version = "0.4.78+curl-8.11.0"
|
||||
version = "0.4.83+curl-8.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eec768341c5c7789611ae51cf6c459099f22e64a5d5d0ce4892434e33821eaf"
|
||||
checksum = "5830daf304027db10c82632a464879d46a3f7c4ba17a31592657ad16c719b483"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -230,7 +230,7 @@ dependencies = [
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -318,7 +318,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -501,9 +501,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.70"
|
||||
version = "0.10.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6"
|
||||
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
@@ -533,9 +533,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.105"
|
||||
version = "0.9.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc"
|
||||
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -799,7 +799,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -865,7 +865,7 @@ version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -929,12 +929,12 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.8"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -971,7 +971,7 @@ dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -981,7 +981,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1035,9 +1035,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.8"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
@@ -1086,15 +1086,6 @@ dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
||||
28
rust/pv/examples/hash_hkd/main.rs
Normal file
28
rust/pv/examples/hash_hkd/main.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use pv::request::EcPubKeyCoord;
|
||||
use pv_core::misc::encode_hex;
|
||||
use s390_pv as pv;
|
||||
|
||||
use std::env::args;
|
||||
|
||||
use pv::misc::{read_certs, read_file};
|
||||
use pv::{Error, Result};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let hkd = args().nth(1).expect("Expect one Host-key document");
|
||||
let hkd_bin = read_file(&hkd, "Host-key document")?;
|
||||
let certs = read_certs(hkd_bin).map_err(|source| Error::HkdNotPemOrDer { hkd, source })?;
|
||||
let hkd_cert = certs
|
||||
.first()
|
||||
.expect("Expect at least one certificate in the HKD file");
|
||||
|
||||
let pc: EcPubKeyCoord = hkd_cert
|
||||
.public_key()
|
||||
.expect("Expected a public key in the Host-key document")
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
println!("{}", encode_hex(pc.sha256().unwrap()));
|
||||
Ok(())
|
||||
}
|
||||
@@ -7,7 +7,7 @@ DEBUG_FILES := $(addsuffix .debug,$(FILES))
|
||||
ifeq ($(HOST_ARCH),s390x)
|
||||
ZIPL_DIR := $(rootdir)/zipl
|
||||
ZIPL_BOOT_DIR := $(ZIPL_DIR)/boot
|
||||
PKGDATADIR := $(TOOLS_DATADIR)/pvimg
|
||||
PVIMG_PKGDATADIR ?= $(TOOLS_DATADIR)/pvimg
|
||||
|
||||
INCLUDE_PATHS := $(ZIPL_BOOT_DIR) $(ZIPL_DIR)/include $(rootdir)/include
|
||||
INCLUDE_PARMS := $(addprefix -I,$(INCLUDE_PATHS))
|
||||
@@ -86,9 +86,9 @@ stage3b.elf: head.o $(ZIPL_OBJS)
|
||||
@chmod a-x $@
|
||||
|
||||
install: stage3a.bin stage3b_reloc.bin
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(PKGDATADIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin $(DESTDIR)$(PKGDATADIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin $(DESTDIR)$(PKGDATADIR)
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(PVIMG_PKGDATADIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3a.bin $(DESTDIR)$(PVIMG_PKGDATADIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 stage3b_reloc.bin $(DESTDIR)$(PVIMG_PKGDATADIR)
|
||||
|
||||
else
|
||||
# Don't generate the dependency files (see `common.mak` for the
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.\" Copyright 2024 IBM Corp.
|
||||
.\" Copyright 2024, 2025 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
|
||||
.TH "PVIMG-CREATE" "1" "2025-04-24" "s390-tools" "Pvimg Manual"
|
||||
.TH "PVIMG-CREATE" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
@@ -11,23 +11,23 @@ pvimg-create \- Create an IBM Secure Execution image
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.fam C
|
||||
pvimg create [OPTIONS] --kernel <FILE> --output <FILE> --host-key-document <FILE> <--no-verify|--cert <FILE>>
|
||||
pvimg create [OPTIONS] \-\-kernel <FILE> \-\-output <FILE> \-\-host\-key\-document <FILE> <\-\-no\-verify|\-\-cert <FILE>>
|
||||
.fam C
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
Use \fBpvimg\fR to generate a single bootable image file with encrypted and
|
||||
integrity-protected parts. The command requires a kernel image, a host-key
|
||||
document, certificates for the host-key document verification, and an output
|
||||
integrity\-protected parts. The command requires a kernel image, a host\-key
|
||||
document, certificates for the host\-key document verification, and an output
|
||||
file name. Optionally, specify an initial RAM filesystem, and a file containing
|
||||
the kernel parameters. If the command should be run offline, use the
|
||||
\fB\-\-offline\fR option and specify the certificate revocation lists (CRLs) by
|
||||
using the \fB\-\-crl\fR option. Should special circumstances require it, you can
|
||||
optionally specify your own keys for the encryption by using the experimental
|
||||
options. For all certificates, CRLs, and host-key documents, both the PEM and
|
||||
options. For all certificates, CRLs, and host\-key documents, both the PEM and
|
||||
DER input formats are supported. In the resulting image file, a plain text boot
|
||||
loader, the encrypted components for kernel, initial RAM disk, kernel
|
||||
parameters, and the encrypted and integrity-protected header are concatenated.
|
||||
parameters, and the encrypted and integrity\-protected header are concatenated.
|
||||
The header contains metadata necessary for running the guest in protected mode.
|
||||
.PP
|
||||
Use this image file as a kernel image for zipl or for a direct kernel boot using
|
||||
@@ -124,7 +124,8 @@ Overwrite an existing Secure Execution boot image.
|
||||
\-\-cck, \-\-comm\-key <FILE>
|
||||
.RS 4
|
||||
Use the content of FILE as the customer\-communication key (CCK). The file must
|
||||
contain exactly 32 bytes of data.
|
||||
contain exactly 32 bytes of data. In previous versions, this option was called
|
||||
\fB\-\-comm\-key\fR.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
@@ -136,6 +137,13 @@ Secure Execution header protection key is a randomly generated key.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\-\-image\-key <FILE>
|
||||
.RS 4
|
||||
Use the content of FILE as the image encryption key. The file must contain
|
||||
exactly 64 bytes of data.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\-\-enable\-dump
|
||||
.RS 4
|
||||
Enable Secure Execution guest dump support. This option requires the
|
||||
@@ -165,8 +173,8 @@ Add\-secret requests don't have to provide the CCK\-derived extension secret
|
||||
.PP
|
||||
\-\-enable\-cck\-update
|
||||
.RS 4
|
||||
Enable CCK update support. Requires z17 or up. This option cannot be
|
||||
used in conjunction with the \fB\-\-enable\-cck\-extension\-secret\fR option.
|
||||
Enable CCK update support. Requires z17 or up. This option cannot be used in
|
||||
conjunction with the \fB\-\-enable\-cck\-extension\-secret\fR option.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
@@ -256,8 +264,8 @@ These are examples of how to generate an IBM Secure Execution image in
|
||||
\fI\,/boot/secure\-linux\/\fR, using the kernel file \fI\,/boot/vmlinuz\/\fR, the
|
||||
initrd in \fI\,/boot/initrd.img\/\fR, the kernel parameters contained in
|
||||
\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the IBM Z
|
||||
signing key in \fI\,ibm\-z\-host\-key\-signing.crt\/\fR, and the host-key document
|
||||
in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in \fI\,comm\-key\/\fR,
|
||||
signing key in \fI\,ibm\-z\-host\-key\-signing.crt\/\fR, and the host\-key document
|
||||
in \fI\,host_key.crt\/\fR. An AES\-256 GCM key is stored in \fI\,comm\-key\/\fR,
|
||||
which is used when creating a Secure Execution image with guest dump support
|
||||
enabled in the second example.
|
||||
|
||||
@@ -271,11 +279,11 @@ Generate an IBM Secure Execution image with Secure Execution guest dump support:
|
||||
.B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm\-z\-host\-key\-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure\-linux\/\fR \-\-enable\-dump \-\-cck \fI\,comm\-key\fR
|
||||
.SH NOTES
|
||||
.IP "1." 4
|
||||
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.
|
||||
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg\-create\fR(1) command.
|
||||
.IP "2." 4
|
||||
An ELF file cannot be used as a Linux kernel image.
|
||||
.IP "3." 4
|
||||
Remember to re-run \fBzipl\fR after updating an IBM Secure Execution image.
|
||||
Remember to re\-run \fBzipl\fR after updating an IBM Secure Execution image.
|
||||
.SH "SEE ALSO"
|
||||
.sp
|
||||
\fBpvimg\fR(1) \fBzipl\fR(8) \fBqemu\fR(1)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.\" Copyright 2024 IBM Corp.
|
||||
.\" Copyright 2024, 2025 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
|
||||
.TH "PVIMG-INFO" "1" "2024-12-19" "s390-tools" "Pvimg Manual"
|
||||
.TH "PVIMG-INFO" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
@@ -11,7 +11,7 @@ pvimg-info \- Print information about the IBM Secure Execution image
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.fam C
|
||||
pvimg info [OPTIONS] --format <FORMAT> <INPUT>
|
||||
pvimg info [OPTIONS] \-\-format <FORMAT> <INPUT>
|
||||
.fam C
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.\" Copyright 2024 IBM Corp.
|
||||
.\" Copyright 2024, 2025 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
|
||||
.TH "PVIMG-TEST" "1" "2024-12-19" "s390-tools" "Pvimg Manual"
|
||||
.TH "PVIMG-TEST" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
@@ -11,7 +11,7 @@ pvimg-test \- Test different aspects of an existing IBM Secure Execution image
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.fam C
|
||||
pvimg test <--host-key-document <FILE>|--key-hashes[=<FILE>]> <INPUT>
|
||||
pvimg test <\-\-host\-key\-document <FILE>|\-\-key\-hashes[=<FILE>]> <INPUT>
|
||||
.fam C
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.\" Copyright 2024 IBM Corp.
|
||||
.\" Copyright 2024, 2025 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
|
||||
.TH "PVIMG" "1" "2024-12-19" "s390-tools" "Pvimg Manual"
|
||||
.TH "PVIMG" "1" "2025-07-25" "s390-tools" "Pvimg Manual"
|
||||
.nh
|
||||
.ad l
|
||||
.SH NAME
|
||||
@@ -21,21 +21,21 @@ Execution images.
|
||||
.SH "PVIMG COMMANDS"
|
||||
.PP
|
||||
|
||||
\fBpvimg-create(1)\fR
|
||||
\fBpvimg\-create(1)\fR
|
||||
.RS 4
|
||||
Create an IBM Secure Execution image
|
||||
.RE
|
||||
|
||||
.PP
|
||||
|
||||
\fBpvimg-info(1)\fR
|
||||
\fBpvimg\-info(1)\fR
|
||||
.RS 4
|
||||
Print information about the IBM Secure Execution image
|
||||
.RE
|
||||
|
||||
.PP
|
||||
|
||||
\fBpvimg-test(1)\fR
|
||||
\fBpvimg\-test(1)\fR
|
||||
.RS 4
|
||||
Test different aspects of an existing IBM Secure Execution image
|
||||
.RE
|
||||
@@ -83,4 +83,4 @@ line flag, or wrong number of arguments.
|
||||
.RE
|
||||
.SH "SEE ALSO"
|
||||
.sp
|
||||
\fBpvimg-create\fR(1) \fBpvimg-info\fR(1) \fBpvimg-test\fR(1) \fBzipl\fR(8) \fBqemu\fR(1)
|
||||
\fBpvimg\-create\fR(1) \fBpvimg\-info\fR(1) \fBpvimg\-test\fR(1) \fBzipl\fR(8) \fBqemu\fR(1)
|
||||
|
||||
@@ -93,6 +93,42 @@ pub struct ComponentPaths {
|
||||
pub parmfile: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// CLI Argument collection for handling user-provided keys.
|
||||
#[derive(Args, Debug)]
|
||||
#[cfg_attr(test, derive(Default))]
|
||||
pub struct UserKeys {
|
||||
/// Use the content of FILE as the customer-communication key (CCK).
|
||||
///
|
||||
/// The file must contain exactly 32 bytes of data. In previous versions,
|
||||
/// this option was called '--comm-key'.
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "FILE",
|
||||
group = "cck-available",
|
||||
visible_alias = "comm-key"
|
||||
)]
|
||||
pub cck: Option<PathBuf>,
|
||||
|
||||
/// Use the content of FILE as the Secure Execution header protection key.
|
||||
///
|
||||
/// The file must contain exactly 32 bytes of data. If the option is not
|
||||
/// specified, the Secure Execution header protection key is a randomly
|
||||
/// generated key.
|
||||
#[arg(long, value_name = "FILE", alias = "x-header-key")]
|
||||
pub hdr_key: Option<PathBuf>,
|
||||
|
||||
/// Use the content of FILE as the image encryption key.
|
||||
///
|
||||
/// The file must contain exactly 64 bytes of data.
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "FILE",
|
||||
conflicts_with = "disable_image_encryption",
|
||||
alias = "x-comp-key"
|
||||
)]
|
||||
pub image_key: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
#[cfg_attr(test, derive(Default))]
|
||||
#[command(
|
||||
@@ -342,25 +378,8 @@ pub struct CreateBootImageArgs {
|
||||
#[arg(long)]
|
||||
pub overwrite: bool,
|
||||
|
||||
/// Use the content of FILE as the customer-communication key (CCK).
|
||||
///
|
||||
/// The file must contain exactly 32 bytes of data. This option used to be
|
||||
/// called '--comm-key' in previous versions.
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "FILE",
|
||||
group = "cck-available",
|
||||
visible_alias = "comm-key"
|
||||
)]
|
||||
pub cck: Option<PathBuf>,
|
||||
|
||||
/// Use the content of FILE as the Secure Execution header protection key.
|
||||
///
|
||||
/// The file must contain exactly 32 bytes of data. If the option is not
|
||||
/// specified, the Secure Execution header protection key is a randomly
|
||||
/// generated key.
|
||||
#[arg(long, value_name = "FILE", alias = "x-header-key")]
|
||||
pub hdr_key: Option<PathBuf>,
|
||||
#[clap(flatten)]
|
||||
pub keys: UserKeys,
|
||||
|
||||
#[clap(flatten)]
|
||||
pub legacy_flags: CreateBootImageLegacyFlags,
|
||||
@@ -379,11 +398,6 @@ pub struct CreateBootImageExperimentalArgs {
|
||||
#[arg(long, value_name = "DIR", hide(true))]
|
||||
pub x_bootloader_directory: Option<PathBuf>,
|
||||
|
||||
/// Manually set the image components encryption key (experimental option).
|
||||
// Hidden in user documentation.
|
||||
#[arg(long, value_name = "FILE", hide(true))]
|
||||
pub x_comp_key: Option<PathBuf>,
|
||||
|
||||
/// Manually set the PSW address used for the Secure Execution header (experimental option).
|
||||
// Hidden in user documentation.
|
||||
#[arg(long, value_name = "ADDRESS", hide(true))]
|
||||
@@ -527,6 +541,10 @@ mod test {
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-cck-update", ["--enable-cck-update"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-cck-update", ["--disable-cck-update"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("multiple-cck", ["--disable-cck-update", "--cck", "/dev/null"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-comp-key", ["--x-comp-key", "/dev/null"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("image-key", ["--image-key", "/dev/null"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"]),
|
||||
CliOption::new("image-key", ["--image-key", "/dev/null"])])),
|
||||
];
|
||||
let invalid_create_args = [
|
||||
flat_map_collect(remove(mvcanv.clone(), "no-verify")),
|
||||
@@ -558,6 +576,16 @@ mod test {
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("extension", ["--enable-cck-extension-secret"]),
|
||||
CliOption::new("update", ["--enable-cck-update"])])),
|
||||
|
||||
// Image component key cannot be provided multiple times
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-comp-key", ["--x-comp-key", "/dev/null"]),
|
||||
CliOption::new("x-comp-key2", ["--x-comp-key", "/dev/null"])])),
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-comp-key", ["--x-comp-key", "/dev/null"]),
|
||||
CliOption::new("image-key", ["--image-key", "/dev/null"])])),
|
||||
|
||||
// Disable image encryption and providing an image-key is mutually
|
||||
// exclusive.
|
||||
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-image-encryption", ["--disable-image-encryption"]),
|
||||
CliOption::new("image-key", ["--image-key", "/dev/null"])])),
|
||||
];
|
||||
|
||||
let mut genprotimg_valid_args = vec![
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
use pv::{misc::read_file, request::Confidential};
|
||||
|
||||
use crate::cli::CreateBootImageExperimentalArgs;
|
||||
use crate::cli::UserKeys;
|
||||
|
||||
#[macro_export]
|
||||
/// Makes it easier to
|
||||
@@ -23,13 +23,9 @@ pub struct UserProvidedKeys {
|
||||
}
|
||||
|
||||
/// Reads all user provided keys.
|
||||
pub fn read_user_provided_keys(
|
||||
cck_path: Option<&Path>,
|
||||
hdr_key_path: Option<&Path>,
|
||||
experimental_args: &CreateBootImageExperimentalArgs,
|
||||
) -> Result<UserProvidedKeys> {
|
||||
pub fn read_user_provided_keys(keys: &UserKeys) -> Result<UserProvidedKeys> {
|
||||
let components_key = {
|
||||
match &experimental_args.x_comp_key {
|
||||
match &keys.image_key {
|
||||
Some(key_path) => {
|
||||
info!(
|
||||
"Use file '{}' as the image components protection key",
|
||||
@@ -44,7 +40,7 @@ pub fn read_user_provided_keys(
|
||||
}
|
||||
};
|
||||
let aead_key = {
|
||||
match hdr_key_path {
|
||||
match &keys.hdr_key {
|
||||
Some(key_path) => {
|
||||
info!(
|
||||
"Use file '{}' as the Secure Execution header protection",
|
||||
@@ -63,7 +59,7 @@ pub fn read_user_provided_keys(
|
||||
};
|
||||
|
||||
let cck = {
|
||||
match cck_path {
|
||||
match &keys.cck {
|
||||
Some(key_path) => {
|
||||
info!(
|
||||
"Use file '{}' as the customer communication key (CCK)",
|
||||
|
||||
@@ -126,11 +126,7 @@ pub fn create(opt: &CreateBootImageArgs) -> Result<OwnExitCode> {
|
||||
let verified_host_keys = opt
|
||||
.certificate_args
|
||||
.get_verified_hkds("Secure Execution image")?;
|
||||
let user_provided_keys = read_user_provided_keys(
|
||||
opt.cck.as_deref(),
|
||||
opt.hdr_key.as_deref(),
|
||||
&opt.experimental_args,
|
||||
)?;
|
||||
let user_provided_keys = read_user_provided_keys(&opt.keys)?;
|
||||
let (plaintext_flags, secret_flags) = parse_flags(opt)?;
|
||||
|
||||
if plaintext_flags.is_set(PcfV1::NoComponentEncryption) {
|
||||
|
||||
@@ -3,44 +3,15 @@
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use std::{
|
||||
ffi::{CString, OsStr},
|
||||
fs::{File, OpenOptions},
|
||||
ffi::OsStr,
|
||||
fs::{rename, File, OpenOptions},
|
||||
io::{self, Seek, SeekFrom, Write},
|
||||
os::unix::{ffi::OsStrExt, fs::OpenOptionsExt},
|
||||
os::unix::fs::OpenOptionsExt,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use pv::{Error, FileAccessErrorType, PvCoreError, Result};
|
||||
|
||||
/// Rust wrapper for `libc::renameat2`
|
||||
fn renameat2<P: AsRef<Path>, Q: AsRef<Path>>(oldpath: P, newpath: Q, flags: u32) -> io::Result<()> {
|
||||
let oldpath_cstr = CString::new(oldpath.as_ref().as_os_str().as_bytes())?;
|
||||
let oldpath_raw = oldpath_cstr.into_raw();
|
||||
let newpath_cstr = CString::new(newpath.as_ref().as_os_str().as_bytes())?;
|
||||
let newpath_raw = newpath_cstr.into_raw();
|
||||
unsafe {
|
||||
// SAFETY: oldpath_raw and newpath_raw are valid CStrings because they were
|
||||
// generated by the `CString::new` function.
|
||||
let ret = libc::renameat2(
|
||||
libc::AT_FDCWD,
|
||||
oldpath_raw,
|
||||
libc::AT_FDCWD,
|
||||
newpath_raw,
|
||||
flags,
|
||||
);
|
||||
// SAFETY: libc::renameat2 does not modify `newpath_raw` and is
|
||||
// therefore still valid.
|
||||
let _ = CString::from_raw(newpath_raw);
|
||||
// SAFETY: libc::renameat2 does not modify `oldpath_raw` and is
|
||||
// therefore still valid.
|
||||
let _ = CString::from_raw(oldpath_raw);
|
||||
if ret == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This type helps to perform atomic operations by writing to a temporary file
|
||||
/// and renaming it to the actual filename when the [`AtomicFile::finish`]
|
||||
/// function is called. If the [`AtomicFile::finish`] function is never called,
|
||||
@@ -66,7 +37,7 @@ impl<F> AsMut<F> for AtomicFile<F> {
|
||||
}
|
||||
|
||||
/// Enum used for more verbosity.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum AtomicFileOperation {
|
||||
/// Replace existing file
|
||||
Replace,
|
||||
@@ -218,17 +189,16 @@ impl TempPath {
|
||||
}
|
||||
|
||||
fn persist(self, operation: AtomicFileOperation) -> Result<()> {
|
||||
let options = match operation {
|
||||
AtomicFileOperation::Replace => 0,
|
||||
AtomicFileOperation::NoReplace => libc::RENAME_NOREPLACE,
|
||||
};
|
||||
|
||||
renameat2(&self.temp_path, &self.path, options).map_err(|e| {
|
||||
PvCoreError::FileAccessRename {
|
||||
src: self.temp_path.as_ref().to_str().unwrap().to_string(),
|
||||
dst: self.path.as_ref().to_str().unwrap().to_string(),
|
||||
source: e,
|
||||
if let Ok(true) = self.path.try_exists() {
|
||||
if operation == AtomicFileOperation::NoReplace {
|
||||
return Err(Error::Io(io::Error::from(io::ErrorKind::AlreadyExists)));
|
||||
}
|
||||
}
|
||||
|
||||
rename(&self.temp_path, &self.path).map_err(|e| PvCoreError::FileAccessRename {
|
||||
src: self.temp_path.as_ref().to_str().unwrap().to_string(),
|
||||
dst: self.path.as_ref().to_str().unwrap().to_string(),
|
||||
source: e,
|
||||
})?;
|
||||
self.forget();
|
||||
Ok(())
|
||||
|
||||
@@ -32,6 +32,9 @@ declare TYPE
|
||||
declare NAME
|
||||
declare SYSPLEX
|
||||
|
||||
declare PV_GUEST
|
||||
declare -i CPI_PERMIT="$CPI_PERMIT_ON_PVGUEST"
|
||||
|
||||
declare -i DRYRUN=0
|
||||
|
||||
# Exit codes
|
||||
@@ -40,6 +43,7 @@ readonly EXIT_FAILURE=1
|
||||
readonly EXIT_ARG_TOO_LONG=3
|
||||
readonly EXIT_INVALID_CHARS=4
|
||||
readonly EXIT_INVALID_ARGS=5
|
||||
readonly EXIT_NOTCONFIGURED=6
|
||||
|
||||
# Distro-IDs as supported by SE/HMC firmware
|
||||
readonly DISTRO_GENERIC=0
|
||||
@@ -69,6 +73,10 @@ Configure the Control-Program-Information (CPI) settings.
|
||||
-S, --sysplex SYSPLEX Set and commit the sysplex name to SYSPLEX
|
||||
-T, --type TYPE Set and commit OS type to TYPE
|
||||
-v, --version Print version information, then exit
|
||||
--permit-cpi Permit to send Control-Program Identification data of
|
||||
protected virtualization guest to the host (must be
|
||||
specified before any commit option). See also the
|
||||
important note.
|
||||
--commit Ignore all other options and commit any uncommitted
|
||||
values
|
||||
--dry-run Do not actually set or commit anything, but show what
|
||||
@@ -77,7 +85,17 @@ Configure the Control-Program-Information (CPI) settings.
|
||||
uncommitted) values
|
||||
|
||||
Environment variables used for the --defaults option:
|
||||
CPI_SYSTEM_TYPE, CPI_SYSTEM_LEVEL, CPI_SYSTEM_NAME, CPI_SYSPLEX_NAME
|
||||
CPI_SYSTEM_TYPE, CPI_SYSTEM_LEVEL, CPI_SYSTEM_NAME, CPI_SYSPLEX_NAME,
|
||||
CPI_PERMIT_ON_PVGUEST (See also the important note.)
|
||||
|
||||
Important: Set CPI_PERMIT_ON_PVGUEST=1 or use --permit_cpi option only if you
|
||||
trust the host system. Enabling these options allows the host to receive
|
||||
potentially sensitive Control-Program Identification (CPI) data from the
|
||||
protected virtualization guest, including:
|
||||
- system_type
|
||||
- system_level
|
||||
- sysplex_name
|
||||
- system_name
|
||||
|
||||
Available bits for the --set-bit option:
|
||||
kvm: Indicate that system is a KVM host
|
||||
@@ -124,6 +142,19 @@ fail_with()
|
||||
|
||||
cpi_commit()
|
||||
{
|
||||
# Commit Control-Program Identification changes on protected
|
||||
# virtualization guests only if it is permitted by the guest. This
|
||||
# prevents leakage of potentially sensitive information to untrusted
|
||||
# hosts.
|
||||
if [[ -f "/sys/firmware/uv/prot_virt_guest" ]]; then
|
||||
read -r PV_GUEST < "/sys/firmware/uv/prot_virt_guest"
|
||||
if [[ "$PV_GUEST" -eq 1 ]]; then
|
||||
if [[ -z "$CPI_PERMIT" ]] || [[ "$CPI_PERMIT" -ne 1 ]]; then
|
||||
echo "Sending CPI data from secure execution Linux guests is disabled. Use --permit-cpi to enable CPI data." >&2
|
||||
exit "$EXIT_NOTCONFIGURED"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
echo 1 > "$CPI_SET" 2> /dev/null
|
||||
}
|
||||
|
||||
@@ -404,7 +435,7 @@ if [ $# -le 0 ]; then
|
||||
print_parse_error_and_exit
|
||||
fi
|
||||
|
||||
opts=$(getopt -o b:ehL:N:S:T:v -l set-bit:,environment,help,level:,name:,sysplex:,type:,commit,dry-run,show,version -n $PRG -- "$@")
|
||||
opts=$(getopt -o b:ehL:N:S:T:v -l set-bit:,environment,help,level:,name:,sysplex:,type:,commit,dry-run,permit-cpi,show,version -n "$PRG" -- "$@")
|
||||
if [ $? -ne 0 ]; then
|
||||
print_parse_error_and_exit
|
||||
fi
|
||||
@@ -473,6 +504,10 @@ while [ -n $1 ]; do
|
||||
cpi_show
|
||||
exit $EXIT_SUCCESS
|
||||
;;
|
||||
--permit-cpi)
|
||||
CPI_PERMIT=1
|
||||
shift
|
||||
;;
|
||||
--commit)
|
||||
cpi_commit
|
||||
exit $EXIT_SUCCESS
|
||||
|
||||
@@ -1372,17 +1372,18 @@ call_run_command() {
|
||||
# extract the raw_command and set cmd_type, as some shell might split into
|
||||
# several lines restrict the cmd echo to the first line only
|
||||
local raw_cmd=$(echo "${cmd}" | head -1 | sed -ne 's/^\([^[:space:]]*\).*$/\1/p')
|
||||
local cmd_type=$(type ${raw_cmd} | cut -d' ' -sf4,5)
|
||||
local cmd_type=$(type -t ${raw_cmd})
|
||||
|
||||
echo "#######################################################" >> "${logfile}"
|
||||
echo "${USER}@${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}"
|
||||
echo "${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}"
|
||||
|
||||
# check calling command type
|
||||
if [ "X${cmd_type}" = "Xshell builtin" ]; then
|
||||
# command is a builtin (no use of timeout possible)
|
||||
if [ "X${cmd_type}" = "Xbuiltin" ]; then
|
||||
# command is a builtin (use of timeout not possible)
|
||||
eval "${cmd}" >> ${logfile} 2>&1
|
||||
rc=$?
|
||||
elif [ "X${cmd_type}" != "Xnot found" ]; then
|
||||
elif [ "X${cmd_type}" != "X" ]; then
|
||||
# command was found and has a type
|
||||
if [ "x${TIMEOUT_OK}" = "xYES" ]; then
|
||||
eval timeout -k ${TOKS} ${TOS} "${cmd}" >> ${logfile} 2>&1
|
||||
|
||||
@@ -1393,18 +1394,23 @@ call_run_command() {
|
||||
rc=$?
|
||||
fi
|
||||
else
|
||||
echo "${SCRIPTNAME}: Warning: Command \"${raw_cmd}\" not available" >> "${logfile}"
|
||||
echo "Command \"${raw_cmd}\" not available" >> "${logfile}"
|
||||
echo >> "${logfile}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# log a warning on rc not 0 and define return
|
||||
if [ ${rc} ]; then
|
||||
# add return code or new line and log a warning if rc is not 0 and define return
|
||||
if [ ${rc} -eq 0 ]; then
|
||||
echo >> "${logfile}"
|
||||
return 0
|
||||
elif [ "x${TIMEOUT_OK}" = "xYES" ] && [ ${rc} -eq 124 ]; then
|
||||
echo "Warning: \"${cmd}\" cancelled by timeout"
|
||||
echo "-> command cancelled by timeout" >> "${logfile}"
|
||||
return 1
|
||||
else
|
||||
echo "${SCRIPTNAME}: Warning: Command \"${cmd}\" failed" >> "${logfile}"
|
||||
echo >> "${logfile}"
|
||||
echo "rc=${rc}" >> "${logfile}"
|
||||
echo "Warning: Command \"${cmd}\" ended with ${rc}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -37,10 +37,15 @@ EnvironmentFile=@sysconf_path@/sysconfig/cpi
|
||||
# Environment=CPI_SYSPLEX_NAME=
|
||||
# Environment=CPI_SYSTEM_LEVEL=
|
||||
# Environment=CPI_SYSTEM_TYPE=LINUX
|
||||
# Environment=CPI_PERMIT_ON_PVGUEST=
|
||||
|
||||
#
|
||||
# Sending data to the HMC/SE
|
||||
ExecStart=@toolslib_path@/cpictl -e
|
||||
|
||||
# On SEL guests, do not send data by default and treat the respective
|
||||
# return code as success.
|
||||
SuccessExitStatus=NOTCONFIGURED
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
14
vmur/vmur.8
14
vmur/vmur.8
@@ -456,7 +456,7 @@ Identifies the z/VM spool file to be ordered.
|
||||
.IP "1." 3
|
||||
Start z/VM console spooling by issuing:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
# vmcp sp cons start
|
||||
@@ -471,7 +471,7 @@ Close the console file and transfer it to the reader queue, find the spool ID
|
||||
behind the \f(CWFILE\fP keyword in the corresponding CP message.
|
||||
In the example below, the spool ID is 398:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
# vmcp sp cons clo \(rs* rdr
|
||||
@@ -484,7 +484,7 @@ RDR FILE 0398 SENT FROM LINUX025 CON WAS 0398 RECS 1872
|
||||
Read and save the spool file on the Linux file system in the
|
||||
current working directory:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
# vmur re -t 398 linux_cons
|
||||
@@ -496,7 +496,7 @@ current working directory:
|
||||
.IP "1." 3
|
||||
Send parmfile to the z/VM punch queue and transfer it to the reader queue:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
# vmur pun -r /boot/parmfile
|
||||
@@ -509,7 +509,7 @@ Reader file with spoolid 0465 created.
|
||||
Send the Linux kernel image to the z/VM punch queue and
|
||||
transfer it to reader queue:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
# vmur pun -r /boot/vmlinuz -N image
|
||||
@@ -522,7 +522,7 @@ Reader file with spoolid 0466 created.
|
||||
Move the Linux kernel image to the first and parmfile to
|
||||
the second position in the reader queue:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
# vmur or 465
|
||||
@@ -534,7 +534,7 @@ the second position in the reader queue:
|
||||
.IP "4."
|
||||
Prepare re-IPL from the z/VM reader and reboot:
|
||||
|
||||
.ft CW
|
||||
.ft CR
|
||||
.in +0.25in
|
||||
.nf
|
||||
# chreipl ccw 0.0.000c
|
||||
|
||||
@@ -101,9 +101,8 @@ Example: "0192" becomes "0.0.0192".
|
||||
.SH EXAMPLES
|
||||
\fBchccwdev --online 0.0.0192,0.0.0195-0.0.0198\fR
|
||||
.RS
|
||||
After completing all outstanding I/O requests for the devices with bus
|
||||
ID 0.0.0192, 0.0.0195, 0.0.0196, 0.0.0197 and 0.0.0198, tries to set
|
||||
the device offline.
|
||||
Tries to set the devices with the bus ID 0.0.0192, 0.0.0195, 0.0.0196, 0.0.0197
|
||||
and 0.0.0198 online.
|
||||
.RE
|
||||
.P
|
||||
.B chccwdev --attribute readonly=1 --attribute online=1 0.0.0192
|
||||
@@ -121,9 +120,9 @@ measurement block facility.
|
||||
.P
|
||||
.B chccwdev --safeoffline 0.0.0192,0.0.0195-0.0.0198
|
||||
.RS
|
||||
Will try to set the devices with bus ID 0.0.0192, 0.0.0195,
|
||||
0.0.0196, 0.0.0197 and 0.0.0198 offline and finish all outstanding I/O
|
||||
requests before.
|
||||
After completing all outstanding I/O requests for the devices with bus
|
||||
ID 0.0.0192, 0.0.0195, 0.0.0196, 0.0.0197 and 0.0.0198, tries to set
|
||||
the devices offline.
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
|
||||
@@ -285,12 +285,23 @@ Use this option to select all supported columns and keys for output.
|
||||
Scale BPS values by UNIT
|
||||
|
||||
Use this option to specify a value by which bytes-per-seconds (BPS) values -
|
||||
such as read and write throughput - are scaled in table output format. Accepted
|
||||
values are:
|
||||
such as read and write throughput - are scaled in human-readable output.
|
||||
Note that scaling is not applied to values in machine-readable output format as
|
||||
produced with option
|
||||
.BR \-\-format .
|
||||
|
||||
Accepted values are:
|
||||
|
||||
.IP \(bu 3
|
||||
.B auto:
|
||||
Scale automatically to fit value into each column. This is the default.
|
||||
Scale automatically to fit value into each column. To indicate the current scaling factor,
|
||||
an SI-suffix is added to each scaled number (e.g. K for 1000). This is the default.
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B auto-iec:
|
||||
Same as
|
||||
.B auto
|
||||
but using power-of-two based IEC-suffixes (e.g. Ki for 1024).
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.IR number :
|
||||
@@ -299,18 +310,34 @@ Scale by
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B K:
|
||||
Scale by 1024 (KiB)
|
||||
Scale by 1000 (KB)
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B M:
|
||||
Scale by 1,048,576 (MiB)
|
||||
Scale by 1,000,000 (MB)
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B G:
|
||||
Scale by 1,073,741,824 (GiB)
|
||||
Scale by 1,000,000,000 (GB)
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B T:
|
||||
Scale by 1,000,000,000,000 (TB)
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B Ki:
|
||||
Scale by 1024 (KiB)
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B Mi:
|
||||
Scale by 1,048,576 (MiB)
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B Gi:
|
||||
Scale by 1,073,741,824 (GiB)
|
||||
.PP
|
||||
.IP \(bu 3
|
||||
.B Ti:
|
||||
Scale by 1,099,511,627,776 (TiB)
|
||||
.PP
|
||||
.RE
|
||||
|
||||
@@ -265,6 +265,7 @@ static struct {
|
||||
unsigned long unit;
|
||||
char unit_suffix;
|
||||
bool unit_specified;
|
||||
bool unit_iec;
|
||||
} opts;
|
||||
|
||||
/* Per CHPID run-time data. */
|
||||
@@ -336,6 +337,7 @@ static void init_opts(void)
|
||||
opts.unit = UNIT_AUTO;
|
||||
opts.unit_suffix = 0;
|
||||
opts.unit_specified = false;
|
||||
opts.unit_iec = false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -459,6 +461,37 @@ static void parse_cmgs(char *arg)
|
||||
}
|
||||
}
|
||||
|
||||
static bool suffix_to_unit(char *arg, unsigned long *unit_ptr, char *suffix_ptr,
|
||||
bool *iec_ptr)
|
||||
{
|
||||
const char *suffixes = "KMGT";
|
||||
unsigned long unit, base;
|
||||
size_t len = strlen(arg);
|
||||
char suffix;
|
||||
bool iec;
|
||||
int i;
|
||||
|
||||
if (len == 1)
|
||||
iec = false;
|
||||
else if (len == 2 && toupper(arg[1]) == 'I')
|
||||
iec = true;
|
||||
else
|
||||
return false;
|
||||
suffix = (char)toupper(*arg);
|
||||
base = iec ? UNIT_BIN : UNIT_DEC;
|
||||
unit = base;
|
||||
for (i = 0; suffixes[i]; i++) {
|
||||
if (suffix == suffixes[i]) {
|
||||
*unit_ptr = unit;
|
||||
*suffix_ptr = suffix;
|
||||
*iec_ptr = iec;
|
||||
return true;
|
||||
}
|
||||
unit *= base;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a scale unit value in @arg and return the resulting scaling factor.
|
||||
*/
|
||||
@@ -467,22 +500,12 @@ static unsigned long parse_unit(char *arg)
|
||||
unsigned long unit;
|
||||
char *endptr;
|
||||
|
||||
if (strlen(arg) == 1) {
|
||||
opts.unit_suffix = (char)toupper(*arg);
|
||||
switch (opts.unit_suffix) {
|
||||
case 'K':
|
||||
return UNIT_BIN;
|
||||
case 'M':
|
||||
return UNIT_BIN * UNIT_BIN;
|
||||
case 'G':
|
||||
return UNIT_BIN * UNIT_BIN * UNIT_BIN;
|
||||
case 'T':
|
||||
return UNIT_BIN * UNIT_BIN * UNIT_BIN * UNIT_BIN;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (suffix_to_unit(arg, &unit, &opts.unit_suffix, &opts.unit_iec))
|
||||
return unit;
|
||||
if (strcmp(arg, "auto-iec") == 0) {
|
||||
opts.unit_iec = true;
|
||||
return UNIT_AUTO;
|
||||
}
|
||||
opts.unit_suffix = 0;
|
||||
if (strcmp(arg, "auto") == 0)
|
||||
return UNIT_AUTO;
|
||||
/* Parse as number. */
|
||||
@@ -1278,7 +1301,7 @@ static struct util_rec *define_table(void)
|
||||
rec = util_rec_new_wide(NULL);
|
||||
column_for_each_selected(col) {
|
||||
util_rec_def(rec, col->name, UTIL_REC_ALIGN_RIGHT,
|
||||
(int)col->width, "");
|
||||
(int)col->width + (int)col->pad, "");
|
||||
}
|
||||
|
||||
return rec;
|
||||
@@ -1343,7 +1366,7 @@ static void scale_fixed(struct cmg_pair_t *p, unsigned long unit)
|
||||
static void add_pair_value(struct util_rec *table, struct column_t *col,
|
||||
struct cmg_pair_t *pair)
|
||||
{
|
||||
char suffix = 0, str[16];
|
||||
char suffix = 0, suffix2 = opts.unit_iec ? 'i' : 0, str[16];
|
||||
int p;
|
||||
|
||||
if (!pair->valid) {
|
||||
@@ -1355,23 +1378,25 @@ static void add_pair_value(struct util_rec *table, struct column_t *col,
|
||||
suffix = scale_auto(pair, UNIT_DEC);
|
||||
} else if (pair->unit == CMG_BPS) {
|
||||
if (opts.unit == UNIT_AUTO)
|
||||
suffix = scale_auto(pair, UNIT_BIN);
|
||||
suffix = scale_auto(pair, opts.unit_iec ? UNIT_BIN : UNIT_DEC);
|
||||
else
|
||||
scale_fixed(pair, opts.unit);
|
||||
}
|
||||
|
||||
switch (pair->type) {
|
||||
case CMG_U32:
|
||||
snprintf(str, sizeof(str), "%u%c", pair->value_u32, suffix);
|
||||
snprintf(str, sizeof(str), "%u%c%c", pair->value_u32, suffix,
|
||||
suffix2);
|
||||
break;
|
||||
case CMG_U64:
|
||||
snprintf(str, sizeof(str), "%llu%c", pair->value_u64, suffix);
|
||||
snprintf(str, sizeof(str), "%llu%c%c", pair->value_u64, suffix,
|
||||
suffix2);
|
||||
break;
|
||||
case CMG_FLOAT:
|
||||
/* Find highest precision that fits into @width characters. */
|
||||
for (p = 2; p >= 0; p--) {
|
||||
snprintf(str, sizeof(str), "%.*f%c", p,
|
||||
pair->value_double, suffix);
|
||||
snprintf(str, sizeof(str), "%.*f%c%c", p,
|
||||
pair->value_double, suffix, suffix2);
|
||||
if (strlen(str) <= col->width)
|
||||
break;
|
||||
}
|
||||
@@ -1539,11 +1564,15 @@ static void print_table_header(void)
|
||||
{
|
||||
struct column_t *col;
|
||||
unsigned int i, next_i, num, width, hdr_width;
|
||||
const char *last_hdr1 = NULL;
|
||||
|
||||
/* Print first header line and update column width based on heading. */
|
||||
for (i = 0; (col = column_get_by_index(i, true)); i = next_i) {
|
||||
if (i > 0)
|
||||
printf(" ");
|
||||
if (last_hdr1 && strcmp(last_hdr1, col->hdr1_group) != 0) {
|
||||
col->pad = 1;
|
||||
printf("%*s", col->pad + 1, "");
|
||||
}
|
||||
last_hdr1 = col->hdr1_group;
|
||||
get_hdr_group_size(i, &num, &width);
|
||||
if (num == 1) {
|
||||
/* Update column width in case heading is wider. */
|
||||
@@ -1571,7 +1600,7 @@ static void print_table_header(void)
|
||||
i = 0;
|
||||
column_for_each_selected(col) {
|
||||
if (i++ > 0)
|
||||
printf(" ");
|
||||
printf("%*s", col->pad + 1, "");
|
||||
printf("%*s", col->width, col->hdr2);
|
||||
}
|
||||
printf("\n");
|
||||
@@ -1667,8 +1696,8 @@ static void calc_column_widths(void)
|
||||
width = 4; /* "999K" */
|
||||
} else {
|
||||
v = MAX_BPS / (double)opts.unit;
|
||||
snprintf(str, sizeof(str) - 1, "%.0f%c", v,
|
||||
opts.unit_suffix);
|
||||
snprintf(str, sizeof(str) - 1, "%.0f%c%c", v,
|
||||
opts.unit_suffix, opts.unit_iec ? 'i' : 0);
|
||||
width = strlen_u(str);
|
||||
}
|
||||
break;
|
||||
@@ -1678,7 +1707,7 @@ static void calc_column_widths(void)
|
||||
col->width = MAX(col->width, width);
|
||||
/* Double space between groups. */
|
||||
if (last_hdr1 && strcmp(last_hdr1, col->hdr1_group) != 0)
|
||||
col->width++;
|
||||
col->pad++;
|
||||
last_hdr1 = col->hdr1_group;
|
||||
}
|
||||
}
|
||||
@@ -1693,7 +1722,8 @@ static void cmd_table(void)
|
||||
|
||||
apply_column_selection();
|
||||
calc_column_widths();
|
||||
column_update_bps_suffix(opts.unit == UNIT_AUTO, opts.unit_suffix);
|
||||
column_update_bps_suffix(opts.unit == UNIT_AUTO, opts.unit_iec,
|
||||
opts.unit_suffix);
|
||||
|
||||
printf("Collecting initial utilization data\n");
|
||||
update_util_all(true);
|
||||
|
||||
@@ -281,13 +281,17 @@ static void calc_ext_metrics(struct cmg_data_t *data, u32 ticks)
|
||||
if (cmcb->dpu_num_cores == 0)
|
||||
return;
|
||||
|
||||
/* dpu_util = dpu_exec_time_cpc / (t * dpu_num_cores) */
|
||||
/* dpu_util = 100 * dpu_exec_time_cpc / (t * dpu_num_cores) */
|
||||
delta = field_delta(dpu_exec_time_cpc, ext_a, ext_b);
|
||||
m->dpu_util = delta / ((double)ticks * cmcb->dpu_num_cores);
|
||||
m->dpu_util = 100.0 * delta / ((double)ticks * cmcb->dpu_num_cores);
|
||||
|
||||
/* dpu_util_total = dpu_channel_exec_time_cpc / (t * dpu_num_cores) */
|
||||
/*
|
||||
* dpu_util_total = 100 * dpu_channel_exec_time_cpc /
|
||||
* (t * dpu_num_cores)
|
||||
*/
|
||||
delta = field_delta(dpu_channel_exec_time_cpc, ext_a, ext_b);
|
||||
m->dpu_util_total = delta / ((double)ticks * cmcb->dpu_num_cores);
|
||||
m->dpu_util_total = 100.0 * delta /
|
||||
((double)ticks * cmcb->dpu_num_cores);
|
||||
|
||||
/* dpu_util_part = dpu_util_total * channel_work_units /
|
||||
* channel_work_units_cpc */
|
||||
|
||||
@@ -350,13 +350,17 @@ static void calc_ext_metrics(struct cmg_data_t *data, double seconds, u32 ticks)
|
||||
if (cmcb->dpu_num_cores == 0)
|
||||
return;
|
||||
|
||||
/* dpu_util = dpu_exec_time_cpc / (t * dpu_num_cores) */
|
||||
/* dpu_util = 100 * dpu_exec_time_cpc / (t * dpu_num_cores) */
|
||||
delta = field_delta(dpu_exec_time_cpc, ext_a, ext_b);
|
||||
m->dpu_util = delta / ((double)ticks * cmcb->dpu_num_cores);
|
||||
m->dpu_util = 100.0 * delta / ((double)ticks * cmcb->dpu_num_cores);
|
||||
|
||||
/* dpu_util_total = dpu_channel_exec_time_cpc / (t * dpu_num_cores) */
|
||||
/*
|
||||
* dpu_util_total = 100 * dpu_channel_exec_time_cpc /
|
||||
* (t * dpu_num_cores)
|
||||
*/
|
||||
delta = field_delta(dpu_channel_exec_time_cpc, ext_a, ext_b);
|
||||
m->dpu_util_total = delta / ((double)ticks * cmcb->dpu_num_cores);
|
||||
m->dpu_util_total = 100.0 * delta /
|
||||
((double)ticks * cmcb->dpu_num_cores);
|
||||
|
||||
/* dpu_util_part = dpu_util_total * channel_work_units /
|
||||
* channel_work_units_cpc */
|
||||
|
||||
@@ -53,6 +53,7 @@ static struct column_t columns[] = {
|
||||
HDR1_CHP_SINGLE,
|
||||
HDR1_CHP_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_TYPE,
|
||||
@@ -63,6 +64,7 @@ static struct column_t columns[] = {
|
||||
HDR1_CHP_SINGLE,
|
||||
HDR1_CHP_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_CMG,
|
||||
@@ -73,6 +75,7 @@ static struct column_t columns[] = {
|
||||
HDR1_CHP_SINGLE,
|
||||
HDR1_CHP_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_SPEED,
|
||||
@@ -83,6 +86,7 @@ static struct column_t columns[] = {
|
||||
HDR1_CHP_SINGLE,
|
||||
HDR1_CHP_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_SHARED,
|
||||
@@ -93,6 +97,7 @@ static struct column_t columns[] = {
|
||||
HDR1_CHP_SINGLE,
|
||||
HDR1_CHP_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_INTERVAL,
|
||||
@@ -103,6 +108,7 @@ static struct column_t columns[] = {
|
||||
HDR1_CHP_SINGLE,
|
||||
HDR1_CHP_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_UTIL_PART,
|
||||
@@ -112,6 +118,7 @@ static struct column_t columns[] = {
|
||||
"PART",
|
||||
HDR1_UTIL_SINGLE,
|
||||
HDR1_UTIL_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -122,6 +129,7 @@ static struct column_t columns[] = {
|
||||
"TOTAL",
|
||||
HDR1_UTIL_SINGLE,
|
||||
HDR1_UTIL_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -133,6 +141,7 @@ static struct column_t columns[] = {
|
||||
HDR1_UTIL_SINGLE,
|
||||
HDR1_UTIL_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_UTIL_DATA,
|
||||
@@ -143,6 +152,7 @@ static struct column_t columns[] = {
|
||||
HDR1_UTIL_SINGLE,
|
||||
HDR1_UTIL_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_READ_PART,
|
||||
@@ -152,6 +162,7 @@ static struct column_t columns[] = {
|
||||
"PART",
|
||||
HDR1_READ_SINGLE,
|
||||
HDR1_READ_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -162,6 +173,7 @@ static struct column_t columns[] = {
|
||||
"TOTAL",
|
||||
HDR1_READ_SINGLE,
|
||||
HDR1_READ_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -172,6 +184,7 @@ static struct column_t columns[] = {
|
||||
"PART",
|
||||
HDR1_WRITE_SINGLE,
|
||||
HDR1_WRITE_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -182,6 +195,7 @@ static struct column_t columns[] = {
|
||||
"TOTAL",
|
||||
HDR1_WRITE_SINGLE,
|
||||
HDR1_WRITE_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -193,6 +207,7 @@ static struct column_t columns[] = {
|
||||
HDR1_FICON_SINGLE,
|
||||
HDR1_FICON_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_FICON_ACTIVE,
|
||||
@@ -203,6 +218,7 @@ static struct column_t columns[] = {
|
||||
HDR1_FICON_SINGLE,
|
||||
HDR1_FICON_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_FICON_DEFER,
|
||||
@@ -213,6 +229,7 @@ static struct column_t columns[] = {
|
||||
HDR1_FICON_SINGLE,
|
||||
HDR1_FICON_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_HPF_RATE,
|
||||
@@ -223,6 +240,7 @@ static struct column_t columns[] = {
|
||||
HDR1_HPF_SINGLE,
|
||||
HDR1_HPF_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_HPF_ACTIVE,
|
||||
@@ -233,6 +251,7 @@ static struct column_t columns[] = {
|
||||
HDR1_HPF_SINGLE,
|
||||
HDR1_HPF_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_HPF_DEFER,
|
||||
@@ -243,6 +262,7 @@ static struct column_t columns[] = {
|
||||
HDR1_HPF_SINGLE,
|
||||
HDR1_HPF_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_MSG_RATE_PART,
|
||||
@@ -252,6 +272,7 @@ static struct column_t columns[] = {
|
||||
"PART",
|
||||
HDR1_MSGR_SINGLE,
|
||||
HDR1_MSGR_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -262,6 +283,7 @@ static struct column_t columns[] = {
|
||||
"TOTAL",
|
||||
HDR1_MSGR_SINGLE,
|
||||
HDR1_MSGR_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -272,6 +294,7 @@ static struct column_t columns[] = {
|
||||
"PART",
|
||||
HDR1_MSGSZ_SINGLE,
|
||||
HDR1_MSGSZ_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -282,6 +305,7 @@ static struct column_t columns[] = {
|
||||
"TOTAL",
|
||||
HDR1_MSGSZ_SINGLE,
|
||||
HDR1_MSGSZ_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -293,6 +317,7 @@ static struct column_t columns[] = {
|
||||
"SNDFAIL",
|
||||
"SNDFAIL",
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_RCV_FAIL_PART,
|
||||
@@ -302,6 +327,7 @@ static struct column_t columns[] = {
|
||||
"PART",
|
||||
HDR1_MSG_RCVF_SINGLE,
|
||||
HDR1_MSG_RCVF_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -312,6 +338,7 @@ static struct column_t columns[] = {
|
||||
"TOTAL",
|
||||
HDR1_MSG_RCVF_SINGLE,
|
||||
HDR1_MSG_RCVF_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -323,6 +350,7 @@ static struct column_t columns[] = {
|
||||
HDR1_DPU_UTIL_SINGLE,
|
||||
HDR1_DPU_UTIL_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_DPU_UTIL,
|
||||
@@ -333,6 +361,7 @@ static struct column_t columns[] = {
|
||||
HDR1_DPU_UTIL_SINGLE,
|
||||
HDR1_DPU_UTIL_GROUP,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
COL_DPU_UTIL_PART,
|
||||
@@ -342,6 +371,7 @@ static struct column_t columns[] = {
|
||||
"PART",
|
||||
HDR1_DPU_UTIL_SINGLE,
|
||||
HDR1_DPU_UTIL_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
{
|
||||
@@ -352,6 +382,7 @@ static struct column_t columns[] = {
|
||||
"TOTAL",
|
||||
HDR1_DPU_UTIL_SINGLE,
|
||||
HDR1_DPU_UTIL_GROUP,
|
||||
5,
|
||||
0,
|
||||
},
|
||||
};
|
||||
@@ -447,15 +478,17 @@ void column_select_all(void)
|
||||
column_select(col);
|
||||
}
|
||||
|
||||
void column_update_bps_suffix(bool auto_scale, char suffix_char)
|
||||
void column_update_bps_suffix(bool auto_scale, bool iec, char suffix_char)
|
||||
{
|
||||
struct column_t *col;
|
||||
char *str;
|
||||
|
||||
if (auto_scale)
|
||||
util_asprintf(&str, "(B/s)");
|
||||
else if (suffix_char)
|
||||
else if (suffix_char && iec)
|
||||
util_asprintf(&str, "(%ciB/s)", suffix_char);
|
||||
else if (suffix_char)
|
||||
util_asprintf(&str, "(%cB/s)", suffix_char);
|
||||
else
|
||||
str = util_strdup("(*)");
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ struct column_t {
|
||||
const char *hdr1_single;
|
||||
char *hdr1_group;
|
||||
unsigned int width;
|
||||
unsigned int pad;
|
||||
};
|
||||
|
||||
#define column_for_each(c) \
|
||||
@@ -83,7 +84,7 @@ void column_select_id_list(const int *ids);
|
||||
void column_select_default(void);
|
||||
void column_select_all(void);
|
||||
|
||||
void column_update_bps_suffix(bool auto_scale, char suffix_char);
|
||||
void column_update_bps_suffix(bool auto_scale, bool iec, char suffix_char);
|
||||
void column_exit(void);
|
||||
|
||||
#endif /* COLUMN_H */
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# Copy zdev persistent config from initrd to root-fs.
|
||||
#
|
||||
|
||||
chzdev --export /run/zdev.initrd.config --all --type --persistent --quiet
|
||||
chzdev --export /run/zdev.initrd.config --configured --type --persistent --quiet
|
||||
|
||||
# Apart from debugging purposes, this is useful for distro installers,
|
||||
# which can import the early config into their own environment early
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
. ds pfont \fP
|
||||
. nh
|
||||
. na
|
||||
. ft CW
|
||||
. ft CR
|
||||
\\$*
|
||||
. ft \\*[pfont]
|
||||
. ad
|
||||
@@ -636,6 +636,15 @@ option unless chzdev is run in an environment where udev is not fully
|
||||
functional (such as in the early phase of an initial RAM disk).
|
||||
.PP
|
||||
.
|
||||
.OD no-module-load "" ""
|
||||
Do not load kernel modules during chzdev execution.
|
||||
|
||||
In some situations \fBchzdev\fR will attempt to load kernel modules to
|
||||
complete a configuration action. This behavior may be undesirable in some
|
||||
scenarios. The \fB\-\-no-module-load\fR option suppresses kernel module loading
|
||||
during \fBchzdev\fR execution.
|
||||
.PP
|
||||
.
|
||||
.OD persistent "p" ""
|
||||
Apply changes to persistent configuration only.
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
. ds pfont \fP
|
||||
. nh
|
||||
. na
|
||||
. ft CW
|
||||
. ft CR
|
||||
\\$*
|
||||
. ft \\*[pfont]
|
||||
. ad
|
||||
|
||||
@@ -109,6 +109,7 @@ struct options {
|
||||
unsigned int quiet:1;
|
||||
unsigned int no_settle:1;
|
||||
unsigned int site_id;
|
||||
unsigned int no_module_load:1;
|
||||
};
|
||||
|
||||
/* Makefile converts chzdev_usage.txt into C file which we include here. */
|
||||
@@ -156,6 +157,7 @@ enum {
|
||||
OPT_NO_SETTLE = (OPT_ANONYMOUS_BASE+__COUNTER__),
|
||||
OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__),
|
||||
OPT_SITE = 's',
|
||||
OPT_NO_MODULE_LOAD = (OPT_ANONYMOUS_BASE+__COUNTER__),
|
||||
};
|
||||
|
||||
static struct opts_conflict conflict_list[] = {
|
||||
@@ -260,6 +262,7 @@ static const struct option opt_list[] = {
|
||||
{ "quiet", no_argument, NULL, OPT_QUIET },
|
||||
{ "no-settle", no_argument, NULL, OPT_NO_SETTLE },
|
||||
{ "site", required_argument, NULL, OPT_SITE },
|
||||
{ "no-module-load", no_argument, NULL, OPT_NO_MODULE_LOAD },
|
||||
{ NULL, no_argument, NULL, 0 },
|
||||
};
|
||||
|
||||
@@ -1018,6 +1021,11 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
opts->no_settle = 1;
|
||||
break;
|
||||
|
||||
case OPT_NO_MODULE_LOAD:
|
||||
/* --no-module-load */
|
||||
opts->no_module_load = 1;
|
||||
break;
|
||||
|
||||
case OPT_SITE:
|
||||
/* --site */
|
||||
/* 1. User can specify only site-ids from 0 to 9
|
||||
@@ -3177,6 +3185,7 @@ int main(int argc, char *argv[])
|
||||
dryrun = opts.dryrun;
|
||||
udev_no_settle = opts.no_settle;
|
||||
path_set_base(opts.base);
|
||||
module_load_suppress(opts.no_module_load);
|
||||
|
||||
if (dryrun)
|
||||
info("Starting dry-run, configuration will not be changed\n");
|
||||
|
||||
@@ -57,6 +57,7 @@ OPTIONS
|
||||
--dry-run Display changes without applying
|
||||
--base PATH Use PATH as base for accessing files
|
||||
--no-settle Do not wait for udev to settle
|
||||
--no-module-load Do not load kernel modules during execution
|
||||
--auto-conf Apply changes to auto-configuration only
|
||||
-s, --site ID Apply changes to the specified site only
|
||||
--is-owner FILE Examine whether the file is generated by zdev
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "export.h"
|
||||
#include "firmware.h"
|
||||
#include "misc.h"
|
||||
#include "path.h"
|
||||
#include "qeth.h"
|
||||
#include "subtype.h"
|
||||
#include "zfcp_host.h"
|
||||
@@ -143,6 +144,13 @@ struct fw_qeth {
|
||||
char settings[];
|
||||
} __packed;
|
||||
|
||||
/* Dasd types definitions for rd.dasd parser */
|
||||
enum dasd_type {
|
||||
DASD_NO_DEVICE,
|
||||
DASD_ECKD,
|
||||
DASD_NO_ECKD,
|
||||
};
|
||||
|
||||
/* Emit a warning that refers to a position in a firmware file. */
|
||||
static void fwwarn(struct fw_file *f, const char *fmt, ...)
|
||||
{
|
||||
@@ -491,13 +499,49 @@ static struct device *add_device(struct fw_file *f, struct subtype *st,
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* Return the device-type of the provided device-id by analysing modalias */
|
||||
static enum dasd_type is_eckd(const char *id)
|
||||
{
|
||||
const char * const eckd_type[] = { "3390", "3380", "9345" };
|
||||
size_t i;
|
||||
char *device_path, *buffer;
|
||||
int rc = DASD_NO_ECKD;
|
||||
|
||||
/* Remove the device-id from the blacklist */
|
||||
ccw_unblacklist_id(id);
|
||||
|
||||
/* Do a cio_settle before trying to read the modalias */
|
||||
cio_settle(1);
|
||||
|
||||
device_path = path_get_ccw_device(NULL, id);
|
||||
/* Read the modalias value */
|
||||
buffer = path_read_text_file(1, err_ignore, "%s/modalias",
|
||||
device_path);
|
||||
if (!buffer) {
|
||||
rc = DASD_NO_DEVICE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eckd_type); i++) {
|
||||
if (strstr(buffer, eckd_type[i])) {
|
||||
rc = DASD_ECKD;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free(buffer);
|
||||
free(device_path);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Parse a DASD device entry. */
|
||||
static void parse_dasd(struct fw_file *f, struct fw_dehdr *de, config_t config,
|
||||
struct util_list *objects)
|
||||
{
|
||||
struct fw_dasd *dasd = (struct fw_dasd *) de;
|
||||
struct ccw_devid devid;
|
||||
struct device *dev_eckd, *dev_fba;
|
||||
struct device *dev_eckd = NULL, *dev_fba = NULL;
|
||||
char *id;
|
||||
|
||||
if (!check_de_size(f, de, sizeof(struct fw_dasd)))
|
||||
@@ -508,8 +552,22 @@ static void parse_dasd(struct fw_file *f, struct fw_dehdr *de, config_t config,
|
||||
/* Could be either dasd_eckd or dasd_fba - add both entries */
|
||||
io_to_ccw(&devid, &dasd->id);
|
||||
id = ccw_devid_to_str(&devid);
|
||||
dev_eckd = add_device(f, &dasd_subtype_eckd, id, config, objects);
|
||||
dev_fba = add_device(f, &dasd_subtype_fba, id, config, objects);
|
||||
|
||||
switch (is_eckd(id)) {
|
||||
case DASD_ECKD:
|
||||
dev_eckd = add_device(f, &dasd_subtype_eckd, id, config,
|
||||
objects);
|
||||
break;
|
||||
case DASD_NO_ECKD:
|
||||
dev_fba = add_device(f, &dasd_subtype_fba, id, config, objects);
|
||||
break;
|
||||
case DASD_NO_DEVICE:
|
||||
fwwarn(f, "DASD device %s does not exist", id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
free(id);
|
||||
|
||||
if (dasd->hdr.len > sizeof(struct fw_dasd)) {
|
||||
|
||||
@@ -28,7 +28,7 @@ static int suppress_module_load;
|
||||
/* Allow suppression of module loading. */
|
||||
void module_load_suppress(int state)
|
||||
{
|
||||
suppress_module_load = 1;
|
||||
suppress_module_load = state;
|
||||
}
|
||||
|
||||
/* Check if a module is currently loaded. */
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
# generates persistent configuration that can be transferred from initrd to
|
||||
# another root files system such as in a distro installer environment.
|
||||
|
||||
zdev_dasd_base_args="--no-settle --yes --no-root-update --force"
|
||||
zdev_dasd_base_args="--no-settle --yes --no-root-update --no-module-load --quiet"
|
||||
|
||||
zdev_parse_dasd_list() {
|
||||
sed 's/,/\n/g' | while read -r _zdev_dasditem; do
|
||||
@@ -41,7 +41,7 @@ zdev_parse_dasd_list() {
|
||||
echo "rd.dasd ...,${_zdev_dasditem},... :"
|
||||
# shellcheck disable=SC2086
|
||||
chzdev dasd --type "${_zdev_dasditem}=1" --persistent \
|
||||
$zdev_dasd_base_args
|
||||
$zdev_dasd_base_args --force
|
||||
;;
|
||||
"") continue ;; # empty range
|
||||
*) # currently no support for a device-spec "ipldev", only devbusid
|
||||
@@ -72,15 +72,50 @@ EOF
|
||||
$_zdev_dasd_features
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Without dynamic (active) config zdev cannot infer
|
||||
# the actual dasd type (eckd, fba) so configure for both.
|
||||
# the actual dasd type (eckd, fba). Add logic to generate the
|
||||
# udev rules based on the availability of modalias entry.
|
||||
|
||||
echo "rd.dasd ...,${_zdev_dasditem},... :"
|
||||
# shellcheck disable=SC2086
|
||||
chzdev dasd-eckd --enable --persistent "$_zdev_dasd_range" $_zdev_dasd_attrs \
|
||||
$zdev_dasd_base_args
|
||||
# shellcheck disable=SC2086
|
||||
chzdev dasd-fba --enable --persistent "$_zdev_dasd_range" $_zdev_dasd_attrs \
|
||||
$zdev_dasd_base_args
|
||||
|
||||
# free the dev-ids from blacklist to analyse the modalias
|
||||
echo "free $_zdev_dasd_range" > /proc/cio_ignore
|
||||
echo 1 > /proc/cio_settle
|
||||
|
||||
# Now, Configure both DASD types. While doing so, ensure that
|
||||
# module loading is suppressed and that the --force option
|
||||
# is not used with chzdev.
|
||||
# As a result:
|
||||
# - For non-existent devices, udev rules are generated for
|
||||
# both DASD types.
|
||||
# - For existing devices identified as generic-ccw, chzdev
|
||||
# skips generating udev rules.
|
||||
|
||||
for dasd_type in dasd-eckd dasd-fba; do
|
||||
chzdev "$dasd_type" --enable --persistent "$_zdev_dasd_range" \
|
||||
$_zdev_dasd_attrs $zdev_dasd_base_args 2>/dev/null \
|
||||
| sed -e 's/^/Non-existent /g'
|
||||
done
|
||||
|
||||
# For the existing devices, configure the right dasd-type based
|
||||
# on modalias
|
||||
lszdev "$_zdev_dasd_range" --active --no-headings --columns \
|
||||
ID,attr:modalias | \
|
||||
while read -r dev_id modalias_val; do
|
||||
case "$modalias_val" in
|
||||
# ECKD device types
|
||||
*3390*|*3380*|*9345*)
|
||||
chzdev dasd-eckd --enable --persistent "$dev_id" \
|
||||
$_zdev_dasd_attrs $zdev_dasd_base_args --force
|
||||
;;
|
||||
# FBA device types
|
||||
*3370*|*9336*)
|
||||
chzdev dasd-fba --enable --persistent "$dev_id" \
|
||||
$_zdev_dasd_attrs $zdev_dasd_base_args --force
|
||||
;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
esac
|
||||
done # input redir w/ process substitution causes syntax error in dracut env
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "lib/libzds.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_str.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#define COMP "zdsfs: "
|
||||
@@ -1369,18 +1370,6 @@ static void zdsfs_process_device_file(const char *devfile)
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void remove_whitespace(const char *s, char *t)
|
||||
{
|
||||
while (*s != '\0') {
|
||||
if (!isblank(*s)) {
|
||||
*t = *s;
|
||||
t++;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
*t = '\0';
|
||||
}
|
||||
|
||||
static void zdsfs_process_config_file(const char *config)
|
||||
{
|
||||
char line[MAX_LINE_LENGTH];
|
||||
@@ -1403,7 +1392,7 @@ static void zdsfs_process_config_file(const char *config)
|
||||
|
||||
/* remove all whitespaces */
|
||||
tmp = util_malloc(strlen(line) + 1);
|
||||
remove_whitespace(line, tmp);
|
||||
util_str_rm_whitespace(line, tmp);
|
||||
|
||||
key = strtok(tmp, delimiter);
|
||||
if (strcmp(key, "restserver") == 0) {
|
||||
@@ -1496,7 +1485,7 @@ static int zdsfs_process_dataset_conf(const char *config)
|
||||
linecount++;
|
||||
/* remove all whitespaces */
|
||||
tmp = util_malloc(strlen(line) + 1);
|
||||
remove_whitespace(line, tmp);
|
||||
util_str_rm_whitespace(line, tmp);
|
||||
/* skip empty lines */
|
||||
if (*tmp == '\n' || *tmp == '#') {
|
||||
free(tmp);
|
||||
|
||||
@@ -36,8 +36,10 @@ static int open_dump_file(void)
|
||||
mount_point = util_strdup("/tmp/zdump-ngdump-XXXXXX");
|
||||
|
||||
/* Create a mount point directory */
|
||||
if (mkdtemp(mount_point) == NULL)
|
||||
if (mkdtemp(mount_point) == NULL) {
|
||||
warnx("Could not create directory \"%s\"", mount_point);
|
||||
goto fail_free;
|
||||
}
|
||||
|
||||
if (mount(l.device, mount_point, NGDUMP_FSTYPE, MS_RDONLY, NULL)) {
|
||||
warnx("Could not mount \"%s\" (%s)", l.device, strerror(errno));
|
||||
|
||||
@@ -42,11 +42,19 @@ static int read_meta_from_file(const char *filename, struct ngdump_meta *meta)
|
||||
FILE *fp = NULL;
|
||||
char *line = NULL;
|
||||
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: Reading meta file \"%s\"\n",
|
||||
__func__, filename);
|
||||
|
||||
memset(meta, 0, sizeof(*meta));
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
if (!fp) {
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: Could not open \"%s\" (%s)\n",
|
||||
__func__, filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fscanf(fp, "%m[^\n]\n", &line) == 1) {
|
||||
char *ptr, *param = NULL, *value = NULL;
|
||||
@@ -154,8 +162,11 @@ static int validate_meta(const char *mount_point, struct ngdump_meta *meta)
|
||||
* that the given partition is a valid NGDump partition but with no
|
||||
* dump present.
|
||||
*/
|
||||
if (!meta->file)
|
||||
if (!meta->file) {
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: No dump file present\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
if (!meta->sha256sum) {
|
||||
warnx("Invalid NGDump SHA256 checksum");
|
||||
return -1;
|
||||
@@ -189,13 +200,18 @@ int ngdump_read_meta_from_device(const char *device, struct ngdump_meta *meta)
|
||||
|
||||
/* Create a mount point directory */
|
||||
if (mkdtemp(mount_point) == NULL) {
|
||||
warnx("Could not create directory \"%s\"", mount_point);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = mount(device, mount_point, NGDUMP_FSTYPE, MS_RDONLY, NULL);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: Could not mount \"%s\" (%s)\n",
|
||||
__func__, device, strerror(errno));
|
||||
goto out_rmdir;
|
||||
}
|
||||
|
||||
util_asprintf(&filename, "%s/%s", mount_point, NGDUMP_META_FILENAME);
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
#include "boot/s390.h"
|
||||
|
||||
|
||||
static unsigned char ebcdic_037[256] = {
|
||||
/* EBCDIC-to-ASCII table, using EBCDIC code page 037 by default */
|
||||
static unsigned char ebcdic[256] = {
|
||||
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
|
||||
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
|
||||
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
|
||||
@@ -84,84 +85,25 @@ static unsigned char ebcdic_037[256] = {
|
||||
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
|
||||
};
|
||||
|
||||
static unsigned char ebcdic_500[256] = {
|
||||
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
|
||||
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
|
||||
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
|
||||
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC
|
||||
-ENP ->LF */
|
||||
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
|
||||
/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB
|
||||
-IUS */
|
||||
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||
/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC
|
||||
-INP */
|
||||
0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
|
||||
/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL
|
||||
-SW */
|
||||
0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
|
||||
/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */
|
||||
0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
|
||||
/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */
|
||||
0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
|
||||
/* 0x40 SP RSP ä ---- */
|
||||
0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
|
||||
/* 0x48 . < ( + | */
|
||||
0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
|
||||
/* 0x50 & ---- */
|
||||
0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
|
||||
/* 0x58 ß ! $ * ) ; */
|
||||
0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA,
|
||||
/* 0x60 - / ---- Ä ---- ---- ---- */
|
||||
0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
|
||||
/* 0x68 ---- , % _ > ? */
|
||||
0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
|
||||
/* 0x70 --- ---- ---- ---- ---- ---- ---- */
|
||||
0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||
/* 0x78 * ` : # @ ' = " */
|
||||
0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
|
||||
/* 0x80 * a b c d e f g */
|
||||
0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
||||
/* 0x88 h i ---- ---- ---- */
|
||||
0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
|
||||
/* 0x90 ° j k l m n o p */
|
||||
0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
|
||||
/* 0x98 q r ---- ---- */
|
||||
0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
|
||||
/* 0xA0 ~ s t u v w x */
|
||||
0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
/* 0xA8 y z ---- ---- ---- ---- */
|
||||
0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07,
|
||||
/* 0xB0 ^ ---- § ---- */
|
||||
0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
|
||||
/* 0xB8 ---- [ ] ---- ---- ---- ---- */
|
||||
0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07,
|
||||
/* 0xC0 { A B C D E F G */
|
||||
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
||||
/* 0xC8 H I ---- ö ---- */
|
||||
0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
|
||||
/* 0xD0 } J K L M N O P */
|
||||
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
|
||||
/* 0xD8 Q R ---- ü */
|
||||
0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
|
||||
/* 0xE0 \ S T U V W X */
|
||||
0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
/* 0xE8 Y Z ---- Ö ---- ---- ---- */
|
||||
0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
|
||||
/* 0xF0 0 1 2 3 4 5 6 7 */
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
/* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */
|
||||
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
|
||||
};
|
||||
/* Convert 'ebcdic' table from code page 037 to code page 500 if necessary */
|
||||
void ebcdic_update_table(void)
|
||||
{
|
||||
if (!is_zvm()) {
|
||||
ebcdic[74] = 0x5B; /* ¢ -> [ */
|
||||
ebcdic[79] = 0x21; /* | -> ! */
|
||||
ebcdic[90] = 0x5D; /* ! -> ] */
|
||||
ebcdic[95] = 0x5E; /* ¬ -> ^ */
|
||||
ebcdic[176] = 0xA2; /* ^ -> ¢ */
|
||||
ebcdic[186] = 0xAC; /* [ -> ¬ */
|
||||
ebcdic[187] = 0x7C; /* ] -> | */
|
||||
}
|
||||
}
|
||||
|
||||
void ebcdic_to_ascii(unsigned char *target, const unsigned char *source,
|
||||
unsigned int l)
|
||||
{
|
||||
unsigned char *ebc;
|
||||
unsigned int i;
|
||||
|
||||
ebc = is_zvm() ? ebcdic_037 : ebcdic_500;
|
||||
for (i = 0; i < l; i++)
|
||||
target[i] = ebc[source[i]];
|
||||
target[i] = ebcdic[source[i]];
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
void ebcdic_update_table(void);
|
||||
void ebcdic_to_ascii(unsigned char *target, const unsigned char *source,
|
||||
unsigned int l);
|
||||
|
||||
|
||||
@@ -363,7 +363,7 @@ static char *number(char *buf, char *end, unsigned long val,
|
||||
/*
|
||||
* vsnprintf - Format string and place in a buffer
|
||||
*
|
||||
* This funcion only supports a subset of format options defined in the
|
||||
* This function only supports a subset of format options defined in the
|
||||
* C standard, i.e.
|
||||
* specifiers:
|
||||
* * %s (strings)
|
||||
|
||||
@@ -59,7 +59,7 @@ static int menu_read(void)
|
||||
if (is_zvm())
|
||||
for (i = 0; i < count; i++)
|
||||
temp_area[i] = ebcdic_tolower(temp_area[i]);
|
||||
value = ebcdic_strtoul((char *)temp_area, &endptr, 10);
|
||||
value = ebcdic_strtoul(temp_area, &endptr, 10);
|
||||
|
||||
if ((endptr != temp_area) && (value < BOOT_MENU_ENTRIES - 1) &&
|
||||
(__stage2_params.config[value] != 0)) {
|
||||
@@ -68,6 +68,7 @@ static int menu_read(void)
|
||||
} else {
|
||||
/* no valid config retry */
|
||||
printf(msg_econfig);
|
||||
memset(temp_area, 0, PAGE_SIZE);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
@@ -97,6 +98,13 @@ static int menu_list(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum param_result {
|
||||
NUMBER_FOUND = 0,
|
||||
PRINT_PROMPT = 1,
|
||||
NOTHING_FOUND = 2,
|
||||
SCLP_ERROR = 3,
|
||||
};
|
||||
|
||||
/*
|
||||
* Interpret loadparm
|
||||
*
|
||||
@@ -107,15 +115,20 @@ static int menu_list(void)
|
||||
* 0 - found number to boot, stored in value
|
||||
* 1 - print prompt
|
||||
* 2 - nothing found
|
||||
* 3 - sclp error
|
||||
*/
|
||||
static int menu_param(unsigned long *value)
|
||||
static enum param_result menu_param(unsigned long *value)
|
||||
{
|
||||
char loadparm[PARAM_SIZE];
|
||||
char *endptr;
|
||||
int i;
|
||||
|
||||
if (!sclp_param(loadparm))
|
||||
*value = ebcdic_strtoul(loadparm, &endptr, 10);
|
||||
/* try to fetch loadparms from sclp into 'loadparm' */
|
||||
if (sclp_param(loadparm) != 0)
|
||||
return SCLP_ERROR;
|
||||
|
||||
/* parse number from loadparm */
|
||||
*value = ebcdic_strtoul(loadparm, &endptr, 10);
|
||||
|
||||
/* got number, done */
|
||||
if (endptr != loadparm)
|
||||
@@ -127,39 +140,44 @@ static int menu_param(unsigned long *value)
|
||||
while ((i < PARAM_SIZE) && ecbdic_isspace(loadparm[i]))
|
||||
i++;
|
||||
|
||||
if (!strncmp(&loadparm[i], "PROMPT", 6)) {
|
||||
*value = 0;
|
||||
if (!strncmp(&loadparm[i], "PROMPT", 6))
|
||||
return PRINT_PROMPT;
|
||||
}
|
||||
|
||||
return NOTHING_FOUND;
|
||||
}
|
||||
|
||||
int menu(void)
|
||||
{
|
||||
unsigned long value = 0;
|
||||
enum { DEFAULT_MENU_ENTRY = 0 };
|
||||
unsigned long value = DEFAULT_MENU_ENTRY;
|
||||
char *cmd_line_extra;
|
||||
char endstring[15];
|
||||
int rc;
|
||||
|
||||
cmd_line_extra = (char *)COMMAND_LINE_EXTRA;
|
||||
memset(cmd_line_extra, 0, COMMAND_LINE_EXTRA_SIZE);
|
||||
|
||||
rc = sclp_setup(SCLP_INIT);
|
||||
if (rc)
|
||||
if (sclp_setup(SCLP_INIT) != 0) {
|
||||
/* sclp setup failed boot default */
|
||||
goto boot;
|
||||
}
|
||||
|
||||
rc = menu_param(&value);
|
||||
if (rc == 0) {
|
||||
switch (menu_param(&value)) {
|
||||
case NUMBER_FOUND:
|
||||
/* got number from loadparm, boot it */
|
||||
goto boot;
|
||||
} else if (rc == 1 && value == 0) {
|
||||
/* keyword "prompt", show menu */
|
||||
} else if (__stage2_params.flag == 0) {
|
||||
/* menu disabled, boot default */
|
||||
value = 0;
|
||||
case PRINT_PROMPT:
|
||||
/* print menu */
|
||||
break;
|
||||
case SCLP_ERROR:
|
||||
/* failed to read from sclp, boot default */
|
||||
printf("SCLP_ERROR\n");
|
||||
goto boot;
|
||||
case NOTHING_FOUND:
|
||||
if (__stage2_params.flag == 0) {
|
||||
/* menu disabled, boot default */
|
||||
goto boot;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* print banner */
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
#define PARAM_SIZE 8
|
||||
#define TEXT_OFFSET 4
|
||||
|
||||
#define NUMBER_FOUND 0
|
||||
#define PRINT_PROMPT 1
|
||||
#define NOTHING_FOUND 2
|
||||
|
||||
struct boot_stage2_params {
|
||||
uint16_t flag;
|
||||
uint16_t timeout;
|
||||
|
||||
@@ -507,6 +507,8 @@ void start(void)
|
||||
if (secure_boot_enabled())
|
||||
verify_secure_boot();
|
||||
|
||||
ebcdic_update_table();
|
||||
|
||||
/*
|
||||
* cut the kernel header
|
||||
*/
|
||||
@@ -525,8 +527,12 @@ void start(void)
|
||||
*(unsigned long long *)INITRD_START = _stage3_parms.initrd_addr;
|
||||
*(unsigned long long *)INITRD_SIZE = _stage3_parms.initrd_len;
|
||||
|
||||
/* store address of new kernel to 0 to be able to start it */
|
||||
*(unsigned long long *)0 = _stage3_parms.load_psw;
|
||||
/*
|
||||
* store address of new kernel to 0 to be able to start it.
|
||||
* -fno-delete-null-pointer-checks allows us to dereference 0 here,
|
||||
* which otherwise would be an UB.
|
||||
*/
|
||||
*(volatile unsigned long long *)0 = _stage3_parms.load_psw;
|
||||
|
||||
kdump_stage3();
|
||||
|
||||
|
||||
@@ -66,7 +66,8 @@ makedumpfile -f -E --message-level 7 -d 31 "$VMCORE" "$MNTDIR/$DUMP"
|
||||
# Create a file containing meta information for zgetdump
|
||||
#
|
||||
save_meta
|
||||
|
||||
[ $? -eq 0 ] || { bail_out "Could not update $META"; }
|
||||
save_logs
|
||||
cleanup
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -68,24 +68,33 @@ typedef enum {
|
||||
undefined
|
||||
} definition_t;
|
||||
|
||||
/* Disk information type */
|
||||
/* Physical disk information type */
|
||||
struct disk_info {
|
||||
dev_t disk;
|
||||
disk_type_t type;
|
||||
dev_t device; /* logical device for bootmap creation */
|
||||
dev_t partition;
|
||||
int devno;
|
||||
int partnum;
|
||||
int phy_block_size;
|
||||
int fs_block_size;
|
||||
uint64_t phy_blocks;
|
||||
struct hd_geometry geo;
|
||||
char* name;
|
||||
char* drv_name;
|
||||
definition_t targetbase_def;
|
||||
int is_nvme;
|
||||
dev_t basedisks[MAX_TARGETS]; /* array of physical disks for
|
||||
* bootstrap blocks recording
|
||||
*/
|
||||
};
|
||||
|
||||
/* Logical device information type */
|
||||
struct device_info {
|
||||
dev_t device; /* logical device for bootmap creation */
|
||||
char *name; /* name of the logical device as retrieved
|
||||
* from "/proc/partitions"
|
||||
*/
|
||||
char *drv_name; /* name of the driver managing the logical device
|
||||
* as retrieved from "/proc/devices", or evaluated
|
||||
*/
|
||||
int fs_block_size;
|
||||
struct disk_info base[MAX_TARGETS]; /* array of physical disks for
|
||||
* bootstrap blocks recording
|
||||
*/
|
||||
};
|
||||
|
||||
struct file_range {
|
||||
@@ -95,51 +104,55 @@ struct file_range {
|
||||
|
||||
struct job_target_data;
|
||||
|
||||
int disk_get_info(const char *device, struct job_target_data *target,
|
||||
struct disk_info **info);
|
||||
int disk_get_ext_type(const char *device, struct disk_ext_type *ext_type);
|
||||
int device_get_info(const char *device, struct job_target_data *target,
|
||||
struct device_info **info);
|
||||
int disk_get_ext_type(const char *device, struct disk_ext_type *ext_type,
|
||||
int disk_id);
|
||||
int disk_is_tape(const char *device);
|
||||
int disk_type_is_scsi(struct disk_ext_type *ext_type);
|
||||
int disk_type_is_eckd_ldl(struct disk_ext_type *ext_type);
|
||||
int disk_type_is_nvme(struct disk_ext_type *ext_type);
|
||||
int disk_type_is_eckd(disk_type_t type);
|
||||
|
||||
int disk_info_set_fs_block(const char *filename, struct disk_info *info);
|
||||
int disk_get_info_from_file(const char* filename,
|
||||
struct job_target_data* target,
|
||||
struct disk_info** info);
|
||||
void disk_free_info(struct disk_info* info);
|
||||
char* disk_get_type_name(disk_type_t type);
|
||||
int device_info_set_fs_block(const char *filename, struct device_info *info);
|
||||
int device_get_info_from_file(const char *filename,
|
||||
struct job_target_data *target,
|
||||
struct device_info **info);
|
||||
void device_free_info(struct device_info *info);
|
||||
char *disk_get_type_name(disk_type_t type);
|
||||
char *disk_get_ipl_type(disk_type_t type, int is_dump);
|
||||
int disk_is_large_volume(struct disk_info* info);
|
||||
int disk_cyl_from_blocknum(blocknum_t blocknum, struct disk_info* info);
|
||||
int disk_head_from_blocknum(blocknum_t blocknum, struct disk_info* info);
|
||||
int disk_sec_from_blocknum(blocknum_t blocknum, struct disk_info* info);
|
||||
void disk_blockptr_from_blocknum(disk_blockptr_t* ptr, blocknum_t blocknum,
|
||||
struct disk_info* info);
|
||||
int disk_write_block_aligned(struct misc_fd *mfd, const void *data, size_t bytecount,
|
||||
disk_blockptr_t *block, struct disk_info *info);
|
||||
struct disk_info *info);
|
||||
int disk_write_block_aligned(struct misc_fd *mfd, const void *data,
|
||||
size_t bytecount, disk_blockptr_t *block,
|
||||
int fs_block_size, struct disk_info *info);
|
||||
blocknum_t disk_write_block_buffer(struct misc_fd *fd, int fd_is_basedisk,
|
||||
const void* buffer, size_t bytecount,
|
||||
disk_blockptr_t** blocklist,
|
||||
struct disk_info *info);
|
||||
int fs_block_size, struct disk_info *info);
|
||||
blocknum_t disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk,
|
||||
const void *buffer, size_t bytecount,
|
||||
disk_blockptr_t **blocklist,
|
||||
int fs_block_size,
|
||||
struct disk_info *info, int align,
|
||||
off_t *offset);
|
||||
void disk_print_devt(dev_t d);
|
||||
void disk_print_devname(dev_t d);
|
||||
void prepare_footnote_ptr(int source, char *ptr);
|
||||
void print_footnote_ref(int source, const char *prefix);
|
||||
void disk_print_info(struct disk_info *info, int source);
|
||||
int disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info);
|
||||
void device_print_info(struct device_info *info, struct job_target_data *td);
|
||||
int disk_is_zero_block(disk_blockptr_t *block, struct disk_info *info);
|
||||
blocknum_t disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
|
||||
struct disk_info* info);
|
||||
struct disk_info *info);
|
||||
blocknum_t disk_get_blocklist_from_file(const char* filename,
|
||||
struct file_range *reg,
|
||||
disk_blockptr_t** blocklist,
|
||||
struct disk_info* pinfo);
|
||||
disk_blockptr_t **blocklist,
|
||||
int fs_block_size,
|
||||
struct disk_info *info);
|
||||
int disk_check_subchannel_set(int devno, dev_t device, char* dev_name);
|
||||
int fs_map(int fd, uint64_t offset, blocknum_t *mapped, int fs_block_size);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "job.h"
|
||||
#include "misc.h"
|
||||
#include "zipl.h"
|
||||
#include "boot.h"
|
||||
#include "boot/boot_defs.h"
|
||||
|
||||
#define NR_PROGRAM_TABLES NR_BLKPTR_FORMATS
|
||||
@@ -52,6 +53,11 @@ struct component_loc {
|
||||
struct component_footer {
|
||||
component_type type;
|
||||
const char *desc;
|
||||
int fs_block_aligned; /* if true, then the component should
|
||||
* be aligned to fs_block_size boundary
|
||||
* in the bootmap file. Otherwise, to
|
||||
* phy_block_size boundary.
|
||||
*/
|
||||
};
|
||||
|
||||
struct program_component {
|
||||
@@ -66,18 +72,25 @@ struct program_table {
|
||||
blocknum_t stage1b_count;
|
||||
};
|
||||
|
||||
/* Bootloader Installation Set */
|
||||
struct install_set {
|
||||
/* A part of Bootloader Installation Set specific for an individual mirror */
|
||||
struct install_set_mirror {
|
||||
struct program_table tables[NR_PROGRAM_TABLES];
|
||||
struct program_component *components[NR_PROGRAM_COMPONENTS];
|
||||
char *basetmp;
|
||||
unsigned int print_details:1;
|
||||
unsigned int skip_prepare_blocklist:1;
|
||||
};
|
||||
|
||||
/* Bootloader Installation Set */
|
||||
struct install_set {
|
||||
int nr_menu_entries;
|
||||
struct misc_fd mfd;
|
||||
char *basetmp[MAX_TARGETS];
|
||||
struct install_set_mirror mirrors[MAX_TARGETS];
|
||||
struct file_range comp_reg[NR_PROGRAM_COMPONENTS][BOOT_MENU_ENTRIES];
|
||||
char *filename;
|
||||
unsigned int skip_prepare_device:1;
|
||||
unsigned int tmp_filename_created:1;
|
||||
unsigned int skip_prepare:1;
|
||||
unsigned int print_details:1;
|
||||
struct disk_info *info;
|
||||
struct device_info *info;
|
||||
disk_blockptr_t scsi_dump_sb_blockptr;
|
||||
};
|
||||
|
||||
@@ -106,16 +119,36 @@ static inline const char *component_desc_by_id(enum program_component_id id)
|
||||
return component_footers[id].desc;
|
||||
}
|
||||
|
||||
static inline int fs_block_aligned_by_id(enum program_component_id id)
|
||||
{
|
||||
return component_footers[id].fs_block_aligned;
|
||||
}
|
||||
|
||||
static inline struct program_component *get_component(struct install_set *bis,
|
||||
int mirror_id,
|
||||
int i, int j)
|
||||
{
|
||||
return bis->components[i] + j;
|
||||
return bis->mirrors[mirror_id].components[i] + j;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a component as a region in the bootmap file
|
||||
*/
|
||||
static inline struct file_range *get_component_range(struct install_set *bis,
|
||||
int comp_id, int menu_idx)
|
||||
{
|
||||
return &bis->comp_reg[comp_id][menu_idx];
|
||||
}
|
||||
|
||||
static inline struct file_range *get_envblk_range(struct install_set *bis)
|
||||
{
|
||||
return get_component_range(bis, COMPONENT_ID_ENVBLK, 0);
|
||||
}
|
||||
|
||||
int prepare_bootloader(struct job_data *job, struct install_set *bis);
|
||||
int install_bootloader(struct job_data *job, struct install_set *bis);
|
||||
int post_install_bootloader(struct job_data *job, struct install_set *bis);
|
||||
void free_bootloader(struct install_set *bis);
|
||||
void free_bootloader(struct install_set *bis, struct job_data *job);
|
||||
int install_tapeloader(const char* device, const char* image,
|
||||
const char* parmline, const char* ramdisk,
|
||||
address_t image_addr, address_t parm_addr,
|
||||
@@ -127,11 +160,13 @@ int install_mvdump(char* const device[], struct job_target_data* target,
|
||||
|
||||
int install_fba_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
|
||||
blocknum_t *stage1b_count, disk_blockptr_t *stage2_list,
|
||||
blocknum_t stage2_count, struct disk_info *info);
|
||||
blocknum_t stage2_count, int fs_block_size,
|
||||
struct disk_info *info);
|
||||
int install_eckd_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
|
||||
blocknum_t *stage1b_count,
|
||||
disk_blockptr_t *stage2_list,
|
||||
blocknum_t stage2_count, struct disk_info *info);
|
||||
blocknum_t stage2_count, int fs_block_size,
|
||||
struct disk_info *info);
|
||||
int rewind_tape(int fd);
|
||||
|
||||
#endif /* INSTALL_H */
|
||||
|
||||
@@ -34,6 +34,7 @@ int misc_asprintf(char **out, const char *fmt, ...);
|
||||
void* misc_calloc(size_t n, size_t size);
|
||||
char* misc_strdup(const char* s);
|
||||
int misc_open_device(const char *filename, struct misc_fd *mfd, int simulate);
|
||||
int misc_fsync(struct misc_fd *mfd, const char *filename);
|
||||
int misc_read(int fd, void* buffer, size_t count);
|
||||
int misc_read_file(const char* filename, char** buffer, size_t* size,
|
||||
int nil_terminate);
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
|
||||
#define MAX_DUMP_VOLUMES 32
|
||||
#define MAX_TARGETS 32
|
||||
#define FIRST_MIRROR_ID 0
|
||||
|
||||
#define SECURE_BOOT_UNDEFINED -1
|
||||
#define SECURE_BOOT_DISABLED 0
|
||||
|
||||
@@ -104,5 +104,7 @@ logsave "$MNTDIR/makedumpfile.log" makedumpfile -f -E --message-level 7 -d 31 "$
|
||||
# Create a file containing meta information for zgetdump
|
||||
#
|
||||
save_meta
|
||||
[ $? -eq 0 ] || { bail_out "Could not update $META"; }
|
||||
save_logs
|
||||
|
||||
quit
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
385
zipl/src/disk.c
385
zipl/src/disk.c
@@ -104,12 +104,13 @@ read_block_by_offset(int fd, int blksize, uint64_t offset, char *buffer)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int determine_virtblk_type(struct disk_info *data,
|
||||
static int determine_virtblk_type(struct device_info *dev_info,
|
||||
const struct stat *stats)
|
||||
{
|
||||
struct disk_info *data = &dev_info->base[FIRST_MIRROR_ID];
|
||||
int fd, rc, shift, sb;
|
||||
char *device;
|
||||
char *buffer;
|
||||
int fd, rc, shift, sb;
|
||||
|
||||
rc = 0;
|
||||
buffer = (char *) malloc(data->phy_block_size);
|
||||
@@ -131,9 +132,9 @@ static int determine_virtblk_type(struct disk_info *data,
|
||||
if (data->geo.heads == 15) {
|
||||
/* assume DASD */
|
||||
data->partnum = stats->st_rdev & DASD_PARTN_MASK;
|
||||
data->device = stats->st_rdev & ~DASD_PARTN_MASK;
|
||||
dev_info->device = stats->st_rdev & ~DASD_PARTN_MASK;
|
||||
|
||||
rc = misc_temp_dev(data->device, 1, &device);
|
||||
rc = misc_temp_dev(dev_info->device, 1, &device);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
|
||||
@@ -167,7 +168,7 @@ static int determine_virtblk_type(struct disk_info *data,
|
||||
} else {
|
||||
data->type = disk_type_scsi;
|
||||
data->partnum = stats->st_rdev & SCSI_PARTN_MASK;
|
||||
data->device = stats->st_rdev & ~SCSI_PARTN_MASK;
|
||||
dev_info->device = stats->st_rdev & ~SCSI_PARTN_MASK;
|
||||
}
|
||||
|
||||
out_err:
|
||||
@@ -195,6 +196,8 @@ static int set_target_parameters(FILE *fh, struct job_target_data *td)
|
||||
char *error;
|
||||
int i;
|
||||
|
||||
/* reset array of target parameters */
|
||||
memset(td->targets, 0, sizeof(td->targets));
|
||||
/**
|
||||
* Process a stream of 'key=value' pairs and distribute
|
||||
* them into groups.
|
||||
@@ -284,65 +287,28 @@ error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void print_base_disk_params(struct job_target_data *td, int index)
|
||||
{
|
||||
disk_type_t type = get_targettype(td, index);
|
||||
|
||||
if (!verbose)
|
||||
return;
|
||||
{
|
||||
fprintf(stderr, "Base disk '%s':\n", get_targetbase(td, index));
|
||||
fprintf(stderr, " layout........: %s\n", disk_get_type_name(type));
|
||||
}
|
||||
if (disk_type_is_eckd(type)) {
|
||||
fprintf(stderr, " heads.........: %u\n", get_targetheads(td, index));
|
||||
fprintf(stderr, " sectors.......: %u\n", get_targetsectors(td, index));
|
||||
fprintf(stderr, " cylinders.....: %u\n", get_targetcylinders(td, index));
|
||||
}
|
||||
{
|
||||
fprintf(stderr, " start.........: %lu\n", get_targetoffset(td, index));
|
||||
fprintf(stderr, " blksize.......: %u\n", get_targetblocksize(td, index));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set disk info using ready target parameters provided either by
|
||||
* user, or by script
|
||||
*/
|
||||
static int disk_set_info_by_hint(struct job_target_data *td,
|
||||
struct disk_info *data, int fd)
|
||||
static int device_set_info_by_hint(struct job_target_data *td,
|
||||
struct device_info *data, int fd)
|
||||
{
|
||||
int majnum, minnum;
|
||||
struct stat stats;
|
||||
int i;
|
||||
/*
|
||||
* Currently multiple base disks with different parameters
|
||||
* are not supported
|
||||
*/
|
||||
data->devno = -1;
|
||||
data->phy_block_size = get_targetblocksize(td, 0);
|
||||
data->type = get_targettype(td, 0);
|
||||
|
||||
assert(td->nr_targets != 0);
|
||||
for (i = 1; i < td->nr_targets; i++) {
|
||||
if (data->type != get_targettype(td, i) ||
|
||||
data->phy_block_size != get_targetblocksize(td, i)) {
|
||||
print_base_disk_params(td, 0);
|
||||
print_base_disk_params(td, i);
|
||||
error_reason("Inconsistent base disk geometry in target device");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
data->partnum = 0;
|
||||
data->targetbase_def = undefined;
|
||||
|
||||
/* set devno, type, phy_block_size */
|
||||
for (i = 0; i < td->nr_targets; i++) {
|
||||
definition_t defined_as;
|
||||
data->base[i].devno = -1;
|
||||
data->base[i].type = get_targettype(td, i);
|
||||
data->base[i].phy_block_size = get_targetblocksize(td, i);
|
||||
|
||||
if (sscanf(get_targetbase(td, i),
|
||||
"%d:%d", &majnum, &minnum) == 2) {
|
||||
data->basedisks[i] = makedev(majnum, minnum);
|
||||
defined_as = defined_as_device;
|
||||
data->base[i].disk = makedev(majnum, minnum);
|
||||
data->base[i].targetbase_def = defined_as_device;
|
||||
} else {
|
||||
if (stat(get_targetbase(td, i), &stats)) {
|
||||
error_reason(strerror(errno));
|
||||
@@ -356,47 +322,51 @@ static int disk_set_info_by_hint(struct job_target_data *td,
|
||||
get_targetbase(td, i));
|
||||
return -1;
|
||||
}
|
||||
data->basedisks[i] = stats.st_rdev;
|
||||
defined_as = defined_as_name;
|
||||
data->base[i].disk = stats.st_rdev;
|
||||
data->base[i].targetbase_def = defined_as_name;
|
||||
}
|
||||
if (data->targetbase_def != undefined &&
|
||||
data->targetbase_def != defined_as) {
|
||||
if (data->base[i].targetbase_def !=
|
||||
data->base[0].targetbase_def) {
|
||||
error_reason("Target base disks are defined by different ways");
|
||||
return -1;
|
||||
}
|
||||
data->targetbase_def = defined_as;
|
||||
data->base[i].partnum = 0;
|
||||
if (data->base[i].type == disk_type_scsi &&
|
||||
ioctl(fd, NVME_IOCTL_ID) >= 0)
|
||||
data->base[i].is_nvme = 1;
|
||||
}
|
||||
if (data->type == disk_type_scsi && ioctl(fd, NVME_IOCTL_ID) >= 0)
|
||||
data->is_nvme = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate target parameters in the case when no hints were provided
|
||||
*/
|
||||
static int disk_set_info_auto(struct disk_info *data,
|
||||
static int disk_set_info_auto(struct device_info *dev_info,
|
||||
const struct stat *stats, int fd)
|
||||
{
|
||||
struct disk_info *data = &dev_info->base[FIRST_MIRROR_ID];
|
||||
struct dasd_information dasd_info;
|
||||
|
||||
if (ioctl(fd, BLKSSZGET, &data->phy_block_size)) {
|
||||
error_reason("Could not get blocksize");
|
||||
return -1;
|
||||
}
|
||||
if (!data->drv_name) {
|
||||
if (!dev_info->drv_name) {
|
||||
/* Driver name cannot be read */
|
||||
if (ioctl(fd, BIODASDINFO, &dasd_info)) {
|
||||
data->devno = -1;
|
||||
if (data->geo.start) {
|
||||
/* SCSI partition */
|
||||
data->type = disk_type_scsi;
|
||||
data->partnum = stats->st_rdev & SCSI_PARTN_MASK;
|
||||
data->device = stats->st_rdev & ~SCSI_PARTN_MASK;
|
||||
data->partnum =
|
||||
stats->st_rdev & SCSI_PARTN_MASK;
|
||||
dev_info->device =
|
||||
stats->st_rdev & ~SCSI_PARTN_MASK;
|
||||
} else {
|
||||
/* SCSI disk */
|
||||
data->type = disk_type_scsi;
|
||||
data->partnum = 0;
|
||||
data->device = stats->st_rdev;
|
||||
dev_info->device = stats->st_rdev;
|
||||
}
|
||||
} else {
|
||||
/* DASD */
|
||||
@@ -404,9 +374,9 @@ static int disk_set_info_auto(struct disk_info *data,
|
||||
if (disk_determine_dasd_type(data, dasd_info))
|
||||
return -1;
|
||||
data->partnum = stats->st_rdev & DASD_PARTN_MASK;
|
||||
data->device = stats->st_rdev & ~DASD_PARTN_MASK;
|
||||
dev_info->device = stats->st_rdev & ~DASD_PARTN_MASK;
|
||||
}
|
||||
} else if (strcmp(data->drv_name, UTIL_PROC_DEV_ENTRY_DASD) == 0) {
|
||||
} else if (strcmp(dev_info->drv_name, UTIL_PROC_DEV_ENTRY_DASD) == 0) {
|
||||
/* Driver name is 'dasd' */
|
||||
if (ioctl(fd, BIODASDINFO, &dasd_info)) {
|
||||
error_reason("Could not determine DASD type");
|
||||
@@ -416,27 +386,29 @@ static int disk_set_info_auto(struct disk_info *data,
|
||||
if (disk_determine_dasd_type(data, dasd_info))
|
||||
return -1;
|
||||
data->partnum = stats->st_rdev & DASD_PARTN_MASK;
|
||||
data->device = stats->st_rdev & ~DASD_PARTN_MASK;
|
||||
} else if (strcmp(data->drv_name, UTIL_PROC_DEV_ENTRY_SD) == 0) {
|
||||
dev_info->device = stats->st_rdev & ~DASD_PARTN_MASK;
|
||||
} else if (strcmp(dev_info->drv_name, UTIL_PROC_DEV_ENTRY_SD) == 0) {
|
||||
/* Driver name is 'sd' */
|
||||
data->devno = -1;
|
||||
data->type = disk_type_scsi;
|
||||
data->partnum = stats->st_rdev & SCSI_PARTN_MASK;
|
||||
data->device = stats->st_rdev & ~SCSI_PARTN_MASK;
|
||||
dev_info->device = stats->st_rdev & ~SCSI_PARTN_MASK;
|
||||
|
||||
} else if (strcmp(data->drv_name, UTIL_PROC_DEV_ENTRY_VIRTBLK) == 0) {
|
||||
} else if (strcmp(dev_info->drv_name,
|
||||
UTIL_PROC_DEV_ENTRY_VIRTBLK) == 0) {
|
||||
/* Driver name is 'virtblk' */
|
||||
if (ioctl(fd, HDIO_GETGEO, &data->geo) != 0)
|
||||
perror("Could not retrieve disk geometry information.");
|
||||
if (ioctl(fd, BLKSSZGET, &data->phy_block_size) != 0)
|
||||
perror("Could not retrieve blocksize information.");
|
||||
|
||||
if (determine_virtblk_type(data, stats)) {
|
||||
if (determine_virtblk_type(dev_info, stats)) {
|
||||
error_reason("Virtblk device type not clearly "
|
||||
"determined.");
|
||||
return -1;
|
||||
}
|
||||
} else if (strcmp(data->drv_name, UTIL_PROC_DEV_ENTRY_BLKEXT) == 0 &&
|
||||
} else if (strcmp(dev_info->drv_name,
|
||||
UTIL_PROC_DEV_ENTRY_BLKEXT) == 0 &&
|
||||
ioctl(fd, NVME_IOCTL_ID) >= 0) {
|
||||
/* NVMe path, driver name is 'blkext' */
|
||||
data->devno = -1;
|
||||
@@ -444,18 +416,20 @@ static int disk_set_info_auto(struct disk_info *data,
|
||||
data->is_nvme = 1;
|
||||
|
||||
if (util_sys_dev_is_partition(stats->st_rdev)) {
|
||||
if (util_sys_get_base_dev(stats->st_rdev, &data->device))
|
||||
if (util_sys_get_base_dev(stats->st_rdev,
|
||||
&dev_info->device))
|
||||
return -1;
|
||||
data->partnum = util_sys_get_partnum(stats->st_rdev);
|
||||
if (data->partnum == -1)
|
||||
return -1;
|
||||
} else {
|
||||
data->device = stats->st_rdev;
|
||||
dev_info->device = stats->st_rdev;
|
||||
data->partnum = 0;
|
||||
}
|
||||
} else {
|
||||
/* Driver name is unknown */
|
||||
error_reason("Unsupported device driver '%s'", data->drv_name);
|
||||
error_reason("Unsupported device driver '%s'",
|
||||
dev_info->drv_name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -488,7 +462,7 @@ static void set_source_type(struct job_target_data *td,
|
||||
td->source = source_auto;
|
||||
}
|
||||
|
||||
static void set_driver_name(int fd, struct disk_info *info, dev_t device)
|
||||
static void set_driver_name(int fd, struct device_info *info, dev_t device)
|
||||
{
|
||||
struct util_proc_dev_entry dev_entry;
|
||||
|
||||
@@ -555,30 +529,17 @@ static int run_targetbase_script(struct job_target_data *td,
|
||||
* Note: geo.start contains a sector number offset measured in
|
||||
* physical blocks, not sectors (512 bytes)
|
||||
*/
|
||||
static int disk_set_geometry_by_hint(struct job_target_data *td,
|
||||
struct disk_info *data)
|
||||
static int device_set_geometry_by_hint(struct job_target_data *td,
|
||||
struct device_info *data)
|
||||
{
|
||||
int i;
|
||||
/*
|
||||
* Currently multiple base disks with different parameters
|
||||
* are not supported
|
||||
*/
|
||||
data->geo.heads = get_targetheads(td, 0);
|
||||
data->geo.sectors = get_targetsectors(td, 0);
|
||||
data->geo.cylinders = get_targetcylinders(td, 0);
|
||||
data->geo.start = get_targetoffset(td, 0);
|
||||
|
||||
assert(td->nr_targets != 0);
|
||||
for (i = 1; i < td->nr_targets; i++) {
|
||||
if (data->geo.heads != get_targetheads(td, i) ||
|
||||
data->geo.sectors != get_targetsectors(td, i) ||
|
||||
data->geo.cylinders != get_targetcylinders(td, i) ||
|
||||
data->geo.start != get_targetoffset(td, i)) {
|
||||
print_base_disk_params(td, 0);
|
||||
print_base_disk_params(td, i);
|
||||
error_reason("Inconsistent base disk geometry in target device");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < td->nr_targets; i++) {
|
||||
data->base[i].geo.heads = get_targetheads(td, i);
|
||||
data->base[i].geo.sectors = get_targetsectors(td, i);
|
||||
data->base[i].geo.cylinders = get_targetcylinders(td, i);
|
||||
data->base[i].geo.start = get_targetoffset(td, i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -603,7 +564,6 @@ static int disk_set_info_complete(struct job_target_data *td,
|
||||
struct disk_info *data,
|
||||
struct stat *stats, int fd)
|
||||
{
|
||||
struct util_proc_part_entry part_entry;
|
||||
long devsize;
|
||||
|
||||
/* Get size of device in sectors (512 byte) */
|
||||
@@ -631,15 +591,22 @@ static int disk_set_info_complete(struct job_target_data *td,
|
||||
data->geo.start / (data->phy_block_size / 512);
|
||||
if (data->partnum != 0)
|
||||
data->partition = stats->st_rdev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_set_info_complete(struct device_info *dev_info)
|
||||
{
|
||||
struct util_proc_part_entry part_entry;
|
||||
|
||||
/* Try to get device name */
|
||||
if (util_proc_part_get_entry(data->device, &part_entry) == 0) {
|
||||
data->name = misc_strdup(part_entry.name);
|
||||
if (util_proc_part_get_entry(dev_info->device, &part_entry) == 0) {
|
||||
dev_info->name = misc_strdup(part_entry.name);
|
||||
util_proc_part_free_entry(&part_entry);
|
||||
if (data->name == NULL)
|
||||
if (!dev_info->name)
|
||||
return -1;
|
||||
}
|
||||
/* Initialize file system block size with invalid value */
|
||||
data->fs_block_size = -1;
|
||||
dev_info->fs_block_size = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -663,16 +630,17 @@ static int disk_set_info_complete(struct job_target_data *td,
|
||||
* To exclude any user assumptions about the DEVICE, this function
|
||||
* should be called with TD pointing to a zeroed structure.
|
||||
*
|
||||
* DEVICE: logical, or physical device, optionally formated with a
|
||||
* DEVICE: logical, or physical device, optionally formatted with a
|
||||
* file system.
|
||||
*/
|
||||
int disk_get_info(const char *device, struct job_target_data *td,
|
||||
struct disk_info **info)
|
||||
int device_get_info(const char *device, struct job_target_data *td,
|
||||
struct device_info **info)
|
||||
{
|
||||
char *script_file = NULL;
|
||||
struct disk_info *data;
|
||||
struct device_info *data;
|
||||
struct stat stats;
|
||||
int fd;
|
||||
int i;
|
||||
|
||||
if (stat(device, &stats)) {
|
||||
error_reason(strerror(errno));
|
||||
@@ -683,10 +651,10 @@ int disk_get_info(const char *device, struct job_target_data *td,
|
||||
error_reason(strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
data = (struct disk_info *)misc_malloc(sizeof(struct disk_info));
|
||||
data = (struct device_info *)misc_malloc(sizeof(struct device_info));
|
||||
if (!data)
|
||||
goto error;
|
||||
memset((void *)data, 0, sizeof(struct disk_info));
|
||||
memset((void *)data, 0, sizeof(struct device_info));
|
||||
set_driver_name(fd, data, stats.st_rdev);
|
||||
set_source_type(td, data->drv_name, &script_file);
|
||||
switch (td->source) {
|
||||
@@ -696,9 +664,9 @@ int disk_get_info(const char *device, struct job_target_data *td,
|
||||
/* target parameters were set by the script output */
|
||||
assert(target_parameters_are_set(td));
|
||||
|
||||
if (disk_set_geometry_by_hint(td, data))
|
||||
if (device_set_geometry_by_hint(td, data))
|
||||
goto error;
|
||||
if (disk_set_info_by_hint(td, data, fd))
|
||||
if (device_set_info_by_hint(td, data, fd))
|
||||
goto error;
|
||||
data->device = stats.st_rdev;
|
||||
break;
|
||||
@@ -709,20 +677,20 @@ int disk_get_info(const char *device, struct job_target_data *td,
|
||||
*/
|
||||
assert(target_parameters_are_set(td));
|
||||
|
||||
if (disk_set_geometry_by_hint(td, data))
|
||||
if (device_set_geometry_by_hint(td, data))
|
||||
goto error;
|
||||
if (disk_set_info_by_hint(td, data, fd))
|
||||
if (device_set_info_by_hint(td, data, fd))
|
||||
goto error;
|
||||
/*
|
||||
* multiple base disks are not supported
|
||||
* with this source type
|
||||
*/
|
||||
assert(td->nr_targets == 1);
|
||||
data->device = data->basedisks[0];
|
||||
data->device = data->base[FIRST_MIRROR_ID].disk;
|
||||
break;
|
||||
case source_auto:
|
||||
/* no ready target parameters are available */
|
||||
if (disk_set_geometry_auto(fd, data))
|
||||
if (disk_set_geometry_auto(fd, &data->base[FIRST_MIRROR_ID]))
|
||||
goto error;
|
||||
if (disk_set_info_auto(data, &stats, fd))
|
||||
goto error;
|
||||
@@ -730,13 +698,17 @@ int disk_get_info(const char *device, struct job_target_data *td,
|
||||
* multiple base disks are not supported
|
||||
* with this source type
|
||||
*/
|
||||
data->basedisks[0] = data->device;
|
||||
data->base[FIRST_MIRROR_ID].disk = data->device;
|
||||
td->nr_targets = 1;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
if (disk_set_info_complete(td, data, &stats, fd))
|
||||
for (i = 0; i < td->nr_targets; i++) {
|
||||
if (disk_set_info_complete(td, &data->base[i], &stats, fd))
|
||||
goto error;
|
||||
}
|
||||
if (device_set_info_complete(data))
|
||||
goto error;
|
||||
free(script_file);
|
||||
close(fd);
|
||||
@@ -772,17 +744,20 @@ disk_is_tape(const char* device)
|
||||
* partition, etc). In case of success the resulted disk type is
|
||||
* stored in EXT_TYPE.
|
||||
*/
|
||||
int disk_get_ext_type(const char *device, struct disk_ext_type *ext_type)
|
||||
int disk_get_ext_type(const char *device, struct disk_ext_type *ext_type,
|
||||
int disk_idx)
|
||||
{
|
||||
struct job_target_data tmp = {.source = source_unknown};
|
||||
struct device_info *dev_info;
|
||||
struct disk_info *info;
|
||||
|
||||
if (disk_get_info(device, &tmp, &info))
|
||||
if (device_get_info(device, &tmp, &dev_info))
|
||||
return -1;
|
||||
info = &dev_info->base[disk_idx];
|
||||
ext_type->type = info->type;
|
||||
ext_type->is_nvme = info->is_nvme;
|
||||
|
||||
disk_free_info(info);
|
||||
device_free_info(dev_info);
|
||||
free_target_data(&tmp);
|
||||
return 0;
|
||||
}
|
||||
@@ -811,7 +786,7 @@ int disk_type_is_eckd(disk_type_t type)
|
||||
/**
|
||||
* Retrieve and set block size of the file system which contains FILENAME
|
||||
*/
|
||||
int disk_info_set_fs_block(const char *filename, struct disk_info *info)
|
||||
int device_info_set_fs_block(const char *filename, struct device_info *dinfo)
|
||||
{
|
||||
int blocksize;
|
||||
int fd;
|
||||
@@ -830,7 +805,7 @@ int disk_info_set_fs_block(const char *filename, struct disk_info *info)
|
||||
filename);
|
||||
return -1;
|
||||
}
|
||||
info->fs_block_size = blocksize;
|
||||
dinfo->fs_block_size = blocksize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -838,9 +813,9 @@ int disk_info_set_fs_block(const char *filename, struct disk_info *info)
|
||||
* Retrieve disk info of the device which contains FILENAME
|
||||
* and set the filesystem block size
|
||||
*/
|
||||
int disk_get_info_from_file(const char *filename,
|
||||
struct job_target_data *target,
|
||||
struct disk_info **info)
|
||||
int device_get_info_from_file(const char *filename,
|
||||
struct job_target_data *target,
|
||||
struct device_info **info)
|
||||
{
|
||||
struct stat stats;
|
||||
char *device;
|
||||
@@ -851,15 +826,15 @@ int disk_get_info_from_file(const char *filename,
|
||||
}
|
||||
if (misc_temp_dev(stats.st_dev, 1, &device))
|
||||
return -1;
|
||||
if (disk_get_info(device, target, info)) {
|
||||
if (device_get_info(device, target, info)) {
|
||||
misc_free_temp_dev(device);
|
||||
return -1;
|
||||
}
|
||||
misc_free_temp_dev(device);
|
||||
return disk_info_set_fs_block(filename, *info);
|
||||
return device_info_set_fs_block(filename, *info);
|
||||
}
|
||||
|
||||
void disk_free_info(struct disk_info *info)
|
||||
void device_free_info(struct device_info *info)
|
||||
{
|
||||
if (!info)
|
||||
return;
|
||||
@@ -877,28 +852,29 @@ void disk_free_info(struct disk_info *info)
|
||||
* otherwise. */
|
||||
static int
|
||||
disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
|
||||
blocknum_t* physical, struct disk_info* info)
|
||||
blocknum_t *physical, int fs_block_size,
|
||||
struct disk_info *disk_info)
|
||||
{
|
||||
blocknum_t phy_per_fs;
|
||||
blocknum_t mapped;
|
||||
int subblock;
|
||||
|
||||
/* No file system: partition or raw disk */
|
||||
if (info->fs_block_size == -1) {
|
||||
if (fs_block_size == -1) {
|
||||
if (fd_is_basedisk)
|
||||
*physical = logical;
|
||||
else
|
||||
*physical = logical + info->geo.start;
|
||||
*physical = logical + disk_info->geo.start;
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Get mapping in file system blocks
|
||||
*/
|
||||
phy_per_fs = info->fs_block_size / info->phy_block_size;
|
||||
phy_per_fs = fs_block_size / disk_info->phy_block_size;
|
||||
subblock = logical % phy_per_fs;
|
||||
|
||||
if (fs_map(fd, logical * info->phy_block_size,
|
||||
&mapped, info->fs_block_size) != 0)
|
||||
if (fs_map(fd, logical * disk_info->phy_block_size,
|
||||
&mapped, fs_block_size) != 0)
|
||||
return -1;
|
||||
if (mapped == 0) {
|
||||
/* This is a hole in the file */
|
||||
@@ -907,7 +883,7 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
|
||||
/* Convert file system block to physical */
|
||||
*physical = mapped * phy_per_fs + subblock;
|
||||
/* Add partition start */
|
||||
*physical += info->geo.start;
|
||||
*physical += disk_info->geo.start;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -916,7 +892,7 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
|
||||
/* Return the cylinder on which the block number BLOCKNUM is stored on the
|
||||
* CHS device identified by INFO. */
|
||||
int
|
||||
disk_cyl_from_blocknum(blocknum_t blocknum, struct disk_info* info)
|
||||
disk_cyl_from_blocknum(blocknum_t blocknum, struct disk_info *info)
|
||||
{
|
||||
return blocknum / (info->geo.heads * info->geo.sectors);
|
||||
}
|
||||
@@ -925,7 +901,7 @@ disk_cyl_from_blocknum(blocknum_t blocknum, struct disk_info* info)
|
||||
/* Return the head on which the block number BLOCKNUM is stored on the
|
||||
* CHS device identified by INFO. */
|
||||
int
|
||||
disk_head_from_blocknum(blocknum_t blocknum, struct disk_info* info)
|
||||
disk_head_from_blocknum(blocknum_t blocknum, struct disk_info *info)
|
||||
{
|
||||
return (blocknum / info->geo.sectors) % info->geo.heads;
|
||||
}
|
||||
@@ -934,7 +910,7 @@ disk_head_from_blocknum(blocknum_t blocknum, struct disk_info* info)
|
||||
/* Return the sector on which the block number BLOCKNUM is stored on the
|
||||
* CHS device identified by INFO. */
|
||||
int
|
||||
disk_sec_from_blocknum(blocknum_t blocknum, struct disk_info* info)
|
||||
disk_sec_from_blocknum(blocknum_t blocknum, struct disk_info *info)
|
||||
{
|
||||
return blocknum % info->geo.sectors + 1;
|
||||
}
|
||||
@@ -945,7 +921,7 @@ disk_sec_from_blocknum(blocknum_t blocknum, struct disk_info* info)
|
||||
* layout. */
|
||||
void
|
||||
disk_blockptr_from_blocknum(disk_blockptr_t* ptr, blocknum_t blocknum,
|
||||
struct disk_info* info)
|
||||
struct disk_info *info)
|
||||
{
|
||||
switch (info->type) {
|
||||
case disk_type_scsi:
|
||||
@@ -986,8 +962,9 @@ disk_blockptr_from_blocknum(disk_blockptr_t* ptr, blocknum_t blocknum,
|
||||
* otherwise. On success OFFSET contains offset of the first written byte
|
||||
*/
|
||||
static int
|
||||
disk_write_block_aligned_base(struct misc_fd *mfd, int is_base_disk, const void *data,
|
||||
size_t bytecount, disk_blockptr_t *block,
|
||||
disk_write_block_aligned_base(struct misc_fd *mfd, int is_base_disk,
|
||||
const void *data, size_t bytecount,
|
||||
disk_blockptr_t *block, int fs_block_size,
|
||||
struct disk_info *info, int align, off_t *offset)
|
||||
{
|
||||
blocknum_t current_block;
|
||||
@@ -1021,7 +998,7 @@ disk_write_block_aligned_base(struct misc_fd *mfd, int is_base_disk, const void
|
||||
if (block != NULL) {
|
||||
/* Store block pointer */
|
||||
if (disk_get_blocknum(mfd->fd, is_base_disk, current_block,
|
||||
&blocknum, info))
|
||||
&blocknum, fs_block_size, info))
|
||||
return -1;
|
||||
disk_blockptr_from_blocknum(block, blocknum, info);
|
||||
}
|
||||
@@ -1030,11 +1007,13 @@ disk_write_block_aligned_base(struct misc_fd *mfd, int is_base_disk, const void
|
||||
return 0;
|
||||
}
|
||||
|
||||
int disk_write_block_aligned(struct misc_fd *mfd, const void *data, size_t bytecount,
|
||||
disk_blockptr_t *block, struct disk_info *info)
|
||||
int disk_write_block_aligned(struct misc_fd *mfd, const void *data,
|
||||
size_t bytecount, disk_blockptr_t *block,
|
||||
int fs_block_size, struct disk_info *info)
|
||||
{
|
||||
return disk_write_block_aligned_base(mfd, 0, data, bytecount, block,
|
||||
info, info->phy_block_size, NULL);
|
||||
fs_block_size, info,
|
||||
info->phy_block_size, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1047,8 +1026,9 @@ int disk_write_block_aligned(struct misc_fd *mfd, const void *data, size_t bytec
|
||||
* otherwise.
|
||||
*/
|
||||
blocknum_t
|
||||
disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk, const void *buffer,
|
||||
size_t bytecount, disk_blockptr_t **blocklist,
|
||||
disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk,
|
||||
const void *buffer, size_t bytecount,
|
||||
disk_blockptr_t **blocklist, int fs_block_size,
|
||||
struct disk_info *info, int align, off_t *offset)
|
||||
{
|
||||
blocknum_t count;
|
||||
@@ -1061,6 +1041,7 @@ disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk, const voi
|
||||
count = (bytecount + info->phy_block_size - 1) / info->phy_block_size;
|
||||
*blocklist = (disk_blockptr_t *)util_zalloc(sizeof(disk_blockptr_t) *
|
||||
count);
|
||||
|
||||
if (*blocklist == NULL) {
|
||||
close(mfd->fd);
|
||||
return 0;
|
||||
@@ -1073,6 +1054,7 @@ disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk, const voi
|
||||
rc = disk_write_block_aligned_base(mfd, fd_is_basedisk,
|
||||
VOID_ADD(buffer, written),
|
||||
chunk_size, &(*blocklist)[i],
|
||||
fs_block_size,
|
||||
info,
|
||||
i == 0 ? align : info->phy_block_size,
|
||||
&pos);
|
||||
@@ -1085,12 +1067,14 @@ disk_write_block_buffer_align(struct misc_fd *mfd, int fd_is_basedisk, const voi
|
||||
}
|
||||
|
||||
blocknum_t
|
||||
disk_write_block_buffer(struct misc_fd *mfd, int fd_is_basedisk, const void *buffer,
|
||||
size_t bytecount, disk_blockptr_t **blocklist,
|
||||
disk_write_block_buffer(struct misc_fd *mfd, int fd_is_basedisk,
|
||||
const void *buffer, size_t bytecount,
|
||||
disk_blockptr_t **blocklist, int fs_block_size,
|
||||
struct disk_info *info)
|
||||
{
|
||||
return disk_write_block_buffer_align(mfd, fd_is_basedisk, buffer,
|
||||
bytecount, blocklist, info,
|
||||
bytecount, blocklist,
|
||||
fs_block_size, info,
|
||||
info->phy_block_size, NULL);
|
||||
}
|
||||
|
||||
@@ -1167,83 +1151,95 @@ char *disk_get_ipl_type(disk_type_t type, int is_dump)
|
||||
|
||||
/* Return non-zero for ECKD large volumes. */
|
||||
int
|
||||
disk_is_large_volume(struct disk_info* info)
|
||||
disk_is_large_volume(struct disk_info *info)
|
||||
{
|
||||
return (info->type == disk_type_eckd_ldl ||
|
||||
info->type == disk_type_eckd_cdl) &&
|
||||
info->geo.cylinders == 0xfffe;
|
||||
}
|
||||
|
||||
|
||||
/* Print textual representation of INFO contents. */
|
||||
void disk_print_info(struct disk_info *info, int source)
|
||||
static void disk_print_info(struct disk_info *info, int source)
|
||||
{
|
||||
const char *prefix = " ";
|
||||
char footnote[4] = "";
|
||||
|
||||
prepare_footnote_ptr(source, footnote);
|
||||
printf(" Device..........................: ");
|
||||
disk_print_devt(info->device);
|
||||
printf("%sDisk............................: ", prefix);
|
||||
disk_print_devt(info->disk);
|
||||
if (info->targetbase_def == defined_as_device)
|
||||
printf("%s", footnote);
|
||||
printf("\n");
|
||||
if (info->partnum != 0) {
|
||||
printf(" Partition.......................: ");
|
||||
printf("%sPartition.......................: ", prefix);
|
||||
disk_print_devt(info->partition);
|
||||
printf("\n");
|
||||
}
|
||||
if (info->name) {
|
||||
printf(" Device name.....................: %s",
|
||||
info->name);
|
||||
if (info->targetbase_def == defined_as_name)
|
||||
printf("%s", footnote);
|
||||
printf("\n");
|
||||
}
|
||||
if (info->drv_name) {
|
||||
printf(" Device driver name..............: %s\n",
|
||||
info->drv_name);
|
||||
}
|
||||
if (((info->type == disk_type_fba) ||
|
||||
(info->type == disk_type_diag) ||
|
||||
(info->type == disk_type_eckd_ldl) ||
|
||||
(info->type == disk_type_eckd_cdl)) &&
|
||||
(source == source_auto)) {
|
||||
printf(" DASD device number..............: %04x\n",
|
||||
info->devno);
|
||||
printf("%sDASD device number..............: %04x\n",
|
||||
prefix, info->devno);
|
||||
}
|
||||
printf(" Type............................: disk %s\n",
|
||||
(info->partnum != 0) ? "partition" : "device");
|
||||
printf(" Disk layout.....................: %s%s\n",
|
||||
disk_get_type_name(info->type), footnote);
|
||||
printf("%sType............................: disk %s\n",
|
||||
prefix, (info->partnum != 0) ? "partition" : "device");
|
||||
printf("%sDisk layout.....................: %s%s\n",
|
||||
prefix, disk_get_type_name(info->type), footnote);
|
||||
if (disk_type_is_eckd(info->type)) {
|
||||
printf(" Geometry - heads................: %d%s\n",
|
||||
info->geo.heads, footnote);
|
||||
printf(" Geometry - sectors..............: %d%s\n",
|
||||
info->geo.sectors, footnote);
|
||||
printf("%sGeometry - heads................: %d%s\n",
|
||||
prefix, info->geo.heads, footnote);
|
||||
printf("%sGeometry - sectors..............: %d%s\n",
|
||||
prefix, info->geo.sectors, footnote);
|
||||
if (disk_is_large_volume(info)) {
|
||||
/* ECKD large volume. There is not enough information
|
||||
* available in INFO to calculate disk cylinder size. */
|
||||
printf(" Geometry - cylinders............: > 65534\n");
|
||||
printf("%sGeometry - cylinders............: > 65534\n",
|
||||
prefix);
|
||||
} else {
|
||||
printf(" Geometry - cylinders............: %d%s\n",
|
||||
info->geo.cylinders, footnote);
|
||||
printf("%sGeometry - cylinders............: %d%s\n",
|
||||
prefix, info->geo.cylinders, footnote);
|
||||
}
|
||||
}
|
||||
printf(" Geometry - start................: %ld%s\n",
|
||||
info->geo.start, footnote);
|
||||
if (info->fs_block_size >= 0)
|
||||
printf(" File system block size..........: %d\n",
|
||||
info->fs_block_size);
|
||||
printf(" Physical block size.............: %d%s\n",
|
||||
info->phy_block_size, footnote);
|
||||
printf(" Device size in physical blocks..: %ld\n",
|
||||
(long) info->phy_blocks);
|
||||
print_footnote_ref(source, " ");
|
||||
printf("%sGeometry - start................: %ld%s\n",
|
||||
prefix, info->geo.start, footnote);
|
||||
printf("%sPhysical block size.............: %d%s\n",
|
||||
prefix, info->phy_block_size, footnote);
|
||||
printf("%sDisk size in physical blocks....: %ld\n",
|
||||
prefix, (long)info->phy_blocks);
|
||||
print_footnote_ref(source, prefix);
|
||||
}
|
||||
|
||||
void device_print_info(struct device_info *this, struct job_target_data *td)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf(" Device............................: ");
|
||||
disk_print_devt(this->device);
|
||||
printf("\n");
|
||||
|
||||
if (this->name) {
|
||||
printf(" Device name.......................: %s",
|
||||
this->name);
|
||||
printf("\n");
|
||||
}
|
||||
if (this->drv_name) {
|
||||
printf(" Device driver name................: %s\n",
|
||||
this->drv_name);
|
||||
}
|
||||
if (this->fs_block_size >= 0)
|
||||
printf(" File system block size............: %d\n",
|
||||
this->fs_block_size);
|
||||
for (i = 0; i < td->nr_targets; i++) {
|
||||
printf(" Base %d:\n", i + 1);
|
||||
disk_print_info(&this->base[i], td->source);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether a block is a zero block which identifies a hole in a file.
|
||||
* Return non-zero if BLOCK is a zero block, 0 otherwise. */
|
||||
int
|
||||
disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info)
|
||||
disk_is_zero_block(disk_blockptr_t *block, struct disk_info *info)
|
||||
{
|
||||
switch (info->type) {
|
||||
case disk_type_scsi:
|
||||
@@ -1269,7 +1265,7 @@ disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info)
|
||||
* blocks can be merged, 0 otherwise. */
|
||||
static int
|
||||
can_merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
|
||||
struct disk_info* info)
|
||||
struct disk_info *info)
|
||||
{
|
||||
int max_count;
|
||||
|
||||
@@ -1315,7 +1311,7 @@ can_merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
|
||||
* type. */
|
||||
static void
|
||||
merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
|
||||
struct disk_info* info)
|
||||
struct disk_info *info)
|
||||
{
|
||||
switch (info->type) {
|
||||
case disk_type_scsi:
|
||||
@@ -1338,7 +1334,7 @@ merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
|
||||
* number of elements in the list. */
|
||||
blocknum_t
|
||||
disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
|
||||
struct disk_info* info)
|
||||
struct disk_info *info)
|
||||
{
|
||||
blocknum_t i;
|
||||
blocknum_t last;
|
||||
@@ -1365,8 +1361,8 @@ disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
|
||||
*/
|
||||
blocknum_t
|
||||
disk_get_blocklist_from_file(const char *filename, struct file_range *reg,
|
||||
disk_blockptr_t **blocklist,
|
||||
struct disk_info* info)
|
||||
disk_blockptr_t **blocklist, int fs_block_size,
|
||||
struct disk_info *info)
|
||||
{
|
||||
struct stat stats;
|
||||
int fd;
|
||||
@@ -1419,7 +1415,8 @@ disk_get_blocklist_from_file(const char *filename, struct file_range *reg,
|
||||
}
|
||||
/* Build list */
|
||||
for (i = 0; i < blk_count; i++) {
|
||||
if (disk_get_blocknum(fd, 0, blk_off + i, &blocknum, info)) {
|
||||
if (disk_get_blocknum(fd, 0, blk_off + i, &blocknum,
|
||||
fs_block_size, info)) {
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -172,7 +172,8 @@ install_fba_ccw(int fd, disk_blockptr_t *program_table,
|
||||
int
|
||||
install_eckd_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
|
||||
blocknum_t *stage1b_count, disk_blockptr_t *stage2_list,
|
||||
blocknum_t stage2_count, struct disk_info *info)
|
||||
blocknum_t stage2_count, int fs_block_size,
|
||||
struct disk_info *info)
|
||||
{
|
||||
struct boot_eckd_stage1b *stage1b;
|
||||
int stage1b_size, rc = -1;
|
||||
@@ -187,7 +188,8 @@ install_eckd_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
|
||||
if (boot_init_eckd_stage1b(stage1b, stage2_list, stage2_count))
|
||||
goto out_free_stage1b;
|
||||
*stage1b_count = disk_write_block_buffer(mfd, 1, stage1b, stage1b_size,
|
||||
stage1b_list, info);
|
||||
stage1b_list, fs_block_size,
|
||||
info);
|
||||
if (*stage1b_count == 0)
|
||||
goto out_free_stage1b;
|
||||
rc = 0;
|
||||
@@ -291,7 +293,7 @@ static int install_eckd_cdl_ld(int fd, disk_blockptr_t *br,
|
||||
return -1;
|
||||
rc = DRY_RUN_FUNC(misc_write(fd, &vl, sizeof(vl)));
|
||||
if (rc)
|
||||
error_text("Could not update volume lablel 'vol1'");
|
||||
error_text("Could not update volume label 'vol1'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -430,47 +432,39 @@ static int install_bootloader_dump(struct program_table *tables,
|
||||
int install_bootloader(struct job_data *job, struct install_set *bis)
|
||||
{
|
||||
disk_blockptr_t *scsi_dump_sb_blockptr = &bis->scsi_dump_sb_blockptr;
|
||||
struct disk_info *info = bis->info;
|
||||
char footnote[4];
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
if (!info)
|
||||
if (!bis->info)
|
||||
return 0;
|
||||
|
||||
prepare_footnote_ptr(job->target.source, footnote);
|
||||
/* Inform user about what we're up to */
|
||||
printf("Preparing boot device for %s%s: ",
|
||||
disk_get_ipl_type(info->type,
|
||||
job->id == job_dump_partition),
|
||||
job->id == job_dump_partition ? "dump" : "IPL");
|
||||
if (info->name) {
|
||||
printf("%s", info->name);
|
||||
if (info->devno >= 0)
|
||||
printf(" (%04x)", info->devno);
|
||||
printf(".\n");
|
||||
} else if (info->devno >= 0) {
|
||||
printf("%04x.\n", info->devno);
|
||||
} else {
|
||||
disk_print_devt(info->device);
|
||||
printf(".\n");
|
||||
}
|
||||
/* Install independently on each physical target base */
|
||||
printf("Preparing boot device: ");
|
||||
if (bis->info->name)
|
||||
printf("%s", bis->info->name);
|
||||
else
|
||||
disk_print_devt(bis->info->device);
|
||||
printf(".\n");
|
||||
|
||||
/* Install independently on each physical target base */
|
||||
for (i = 0; i < job_get_nr_targets(job); i++) {
|
||||
struct disk_info *info;
|
||||
int fd;
|
||||
|
||||
info = &bis->info->base[i];
|
||||
if (verbose) {
|
||||
printf("Installing on base disk: ");
|
||||
disk_print_devname(info->basedisks[i]);
|
||||
disk_print_devname(info->disk);
|
||||
printf("%s.\n", footnote);
|
||||
}
|
||||
/* Open device file */
|
||||
fd = open(bis->basetmp[i], O_RDWR);
|
||||
fd = open(bis->mirrors[i].basetmp, O_RDWR);
|
||||
if (fd == -1) {
|
||||
error_reason(strerror(errno));
|
||||
error_text("Could not open temporary device file '%s'",
|
||||
bis->basetmp[i]);
|
||||
bis->mirrors[i].basetmp);
|
||||
return -1;
|
||||
}
|
||||
/* Ensure that potential cache inconsistencies between disk and
|
||||
@@ -488,38 +482,41 @@ int install_bootloader(struct job_data *job, struct install_set *bis)
|
||||
* picture in comments above)
|
||||
*/
|
||||
if (job->id == job_dump_partition) {
|
||||
rc = install_bootloader_dump(bis->tables, info,
|
||||
rc = install_bootloader_dump(bis->mirrors[i].tables,
|
||||
info,
|
||||
scsi_dump_sb_blockptr,
|
||||
job_dump_is_ngdump(job),
|
||||
fd);
|
||||
} else {
|
||||
rc = install_bootloader_ipl(bis->tables, info,
|
||||
rc = install_bootloader_ipl(bis->mirrors[i].tables,
|
||||
info,
|
||||
fd);
|
||||
}
|
||||
if (fsync(fd))
|
||||
error_text("Could not sync device file '%s'",
|
||||
bis->basetmp[i]);
|
||||
bis->mirrors[i].basetmp);
|
||||
if (close(fd))
|
||||
error_text("Could not close device file '%s'",
|
||||
bis->basetmp[i]);
|
||||
bis->mirrors[i].basetmp);
|
||||
if (rc)
|
||||
break;
|
||||
if (!dry_run && rc == 0) {
|
||||
if (info->devno >= 0)
|
||||
syslog(LOG_INFO, "Boot loader written to %s (%04x) - %02x:%02x",
|
||||
(bis->info->name ? bis->info->name : "-"),
|
||||
info->devno,
|
||||
major(bis->info->device),
|
||||
minor(bis->info->device));
|
||||
else
|
||||
syslog(LOG_INFO, "Boot loader written to %s - %02x:%02x",
|
||||
(bis->info->name ? bis->info->name : "-"),
|
||||
major(bis->info->device),
|
||||
minor(bis->info->device));
|
||||
}
|
||||
}
|
||||
if (verbose)
|
||||
print_footnote_ref(job->target.source, "");
|
||||
|
||||
if (!dry_run && rc == 0) {
|
||||
if (info->devno >= 0)
|
||||
syslog(LOG_INFO, "Boot loader written to %s (%04x) - "
|
||||
"%02x:%02x",
|
||||
(info->name ? info->name : "-"), info->devno,
|
||||
major(info->device), minor(info->device));
|
||||
else
|
||||
syslog(LOG_INFO, "Boot loader written to %s - "
|
||||
"%02x:%02x",
|
||||
(info->name ? info->name : "-"),
|
||||
major(info->device), minor(info->device));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -784,6 +781,8 @@ install_tapeloader(const char* device, const char* image, const char* parmline,
|
||||
error_text("Could not rewind tape device '%s' to tape", device);
|
||||
rc = -1;
|
||||
}
|
||||
if (!dry_run && fsync(fd))
|
||||
error_text("Could not sync device file '%s'", device);
|
||||
close(fd);
|
||||
return rc;
|
||||
}
|
||||
@@ -791,7 +790,7 @@ install_tapeloader(const char* device, const char* image, const char* parmline,
|
||||
/* Write 64k null bytes with dump signature at offset 512 to
|
||||
* start of dump partition */
|
||||
static int
|
||||
overwrite_partition_start(int fd, struct disk_info* info, int mv_dump_magic)
|
||||
overwrite_partition_start(int fd, struct disk_info *info, int mv_dump_magic)
|
||||
{
|
||||
int rc;
|
||||
unsigned int bytes = 65536;
|
||||
@@ -824,7 +823,7 @@ overwrite_partition_start(int fd, struct disk_info* info, int mv_dump_magic)
|
||||
* Force block size of 4KiB because otherwise there is not enough space
|
||||
* to write the dump tool.
|
||||
*/
|
||||
static int check_eckd_dump_partition(struct disk_info* info)
|
||||
static int check_eckd_dump_partition(struct disk_info *info)
|
||||
{
|
||||
unsigned long long end_blk = info->geo.start + info->phy_blocks - 1;
|
||||
|
||||
@@ -854,8 +853,10 @@ static void eckd_dump_store_param(struct eckd_dump_param *param,
|
||||
param->bpt = info->geo.sectors;
|
||||
}
|
||||
|
||||
static int install_svdump_eckd_ldl(struct misc_fd *mfd, struct disk_info *info,
|
||||
const struct stage2dump_parm_tail *stage2dump_parms)
|
||||
static int
|
||||
install_svdump_eckd_ldl(struct misc_fd *mfd, int fs_block_size,
|
||||
struct disk_info *info,
|
||||
const struct stage2dump_parm_tail *stage2dump_parms)
|
||||
{
|
||||
disk_blockptr_t *stage2_list, *stage1b_list;
|
||||
blocknum_t stage2_count, stage1b_count;
|
||||
@@ -878,11 +879,13 @@ static int install_svdump_eckd_ldl(struct misc_fd *mfd, struct disk_info *info,
|
||||
if (misc_seek(mfd->fd, info->geo.start * info->phy_block_size))
|
||||
goto out_free_stage2;
|
||||
stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size,
|
||||
&stage2_list, info);
|
||||
&stage2_list,
|
||||
fs_block_size, info);
|
||||
if (stage2_count == 0)
|
||||
goto out_free_stage2_list;
|
||||
if (install_eckd_stage1b(mfd, &stage1b_list, &stage1b_count,
|
||||
stage2_list, stage2_count, info))
|
||||
stage2_list, stage2_count, fs_block_size,
|
||||
info))
|
||||
goto out_free_stage2_list;
|
||||
/* Install stage 0 - afterwards we are at stage 1 position*/
|
||||
boot_init_eckd_ldl_stage0(&stage0);
|
||||
@@ -909,8 +912,9 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int install_dump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
|
||||
void *stage2, size_t stage2_size, int mvdump,
|
||||
static int install_dump_eckd_cdl(struct misc_fd *mfd, int fs_block_size,
|
||||
struct disk_info *info, void *stage2,
|
||||
size_t stage2_size, int mvdump,
|
||||
int force)
|
||||
{
|
||||
blocknum_t count, stage2_count, stage1b_count;
|
||||
@@ -934,12 +938,14 @@ static int install_dump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
|
||||
if (misc_seek(mfd->fd, ECKD_CDL_DUMP_REC * info->phy_block_size))
|
||||
goto out;
|
||||
stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size,
|
||||
&stage2_list, info);
|
||||
&stage2_list, fs_block_size,
|
||||
info);
|
||||
if (stage2_count == 0)
|
||||
goto out;
|
||||
/* Install stage 1b behind stage 2*/
|
||||
if (install_eckd_stage1b(mfd, &stage1b_list, &stage1b_count,
|
||||
stage2_list, stage2_count, info))
|
||||
stage2_list, stage2_count, fs_block_size,
|
||||
info))
|
||||
goto out_free_stage2_list;
|
||||
/* Install stage 0 */
|
||||
boot_init_eckd_cdl_stage0(&stage0_cdl);
|
||||
@@ -966,7 +972,8 @@ out:
|
||||
}
|
||||
|
||||
static int
|
||||
install_svdump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
|
||||
install_svdump_eckd_cdl(struct misc_fd *mfd, int fs_block_size,
|
||||
struct disk_info *info,
|
||||
const struct stage2dump_parm_tail *stage2dump_parms)
|
||||
{
|
||||
size_t stage2_size;
|
||||
@@ -975,13 +982,15 @@ install_svdump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
|
||||
|
||||
if (boot_get_eckd_dump_stage2(&stage2, &stage2_size, stage2dump_parms))
|
||||
return -1;
|
||||
rc = install_dump_eckd_cdl(mfd, info, stage2, stage2_size, 0, 0);
|
||||
rc = install_dump_eckd_cdl(mfd, fs_block_size,
|
||||
info, stage2, stage2_size, 0, 0);
|
||||
free(stage2);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
install_mvdump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
|
||||
install_mvdump_eckd_cdl(struct misc_fd *mfd, int fs_block_size,
|
||||
struct disk_info *info,
|
||||
const struct stage2dump_parm_tail *stage2dump_parms,
|
||||
const struct mvdump_parm_table *mv_parm_table)
|
||||
{
|
||||
@@ -993,7 +1002,8 @@ install_mvdump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
|
||||
if (boot_get_eckd_mvdump_stage2(&stage2, &stage2_size, stage2dump_parms,
|
||||
mv_parm_table))
|
||||
return -1;
|
||||
rc = install_dump_eckd_cdl(mfd, info, stage2, stage2_size, 1,
|
||||
rc = install_dump_eckd_cdl(mfd, fs_block_size, info, stage2,
|
||||
stage2_size, 1,
|
||||
stage2dump_parms->mvdump_force);
|
||||
free(stage2);
|
||||
return rc;
|
||||
@@ -1002,7 +1012,8 @@ install_mvdump_eckd_cdl(struct misc_fd *mfd, struct disk_info *info,
|
||||
int
|
||||
install_fba_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
|
||||
blocknum_t *stage1b_count, disk_blockptr_t *stage2_list,
|
||||
blocknum_t stage2_count, struct disk_info *info)
|
||||
blocknum_t stage2_count, int fs_block_size,
|
||||
struct disk_info *info)
|
||||
{
|
||||
struct boot_fba_stage1b *stage1b;
|
||||
int stage1b_size, rc = -1;
|
||||
@@ -1017,7 +1028,8 @@ install_fba_stage1b(struct misc_fd *mfd, disk_blockptr_t **stage1b_list,
|
||||
if (boot_init_fba_stage1b(stage1b, stage2_list, stage2_count))
|
||||
goto out_free_stage1b;
|
||||
*stage1b_count = disk_write_block_buffer(mfd, 1, stage1b, stage1b_size,
|
||||
stage1b_list, info);
|
||||
stage1b_list, fs_block_size,
|
||||
info);
|
||||
if (*stage1b_count == 0)
|
||||
goto out_free_stage1b;
|
||||
rc = 0;
|
||||
@@ -1028,7 +1040,8 @@ out:
|
||||
}
|
||||
|
||||
static int
|
||||
install_svdump_fba(struct misc_fd *mfd, struct disk_info *info,
|
||||
install_svdump_fba(struct misc_fd *mfd, int fs_block_size,
|
||||
struct disk_info *info,
|
||||
const struct stage2dump_parm_tail *stage2dump_parms)
|
||||
{
|
||||
blocknum_t stage1b_count, stage2_count, blk;
|
||||
@@ -1053,7 +1066,8 @@ install_svdump_fba(struct misc_fd *mfd, struct disk_info *info,
|
||||
if (misc_seek(mfd->fd, blk * info->phy_block_size))
|
||||
goto out_free_stage2;
|
||||
stage2_count = disk_write_block_buffer(mfd, 1, stage2, stage2_size,
|
||||
&stage2_list, info);
|
||||
&stage2_list, fs_block_size,
|
||||
info);
|
||||
if (stage2_count == 0)
|
||||
goto out_free_stage2;
|
||||
/* Install stage 1b in front of stage 2 */
|
||||
@@ -1061,7 +1075,7 @@ install_svdump_fba(struct misc_fd *mfd, struct disk_info *info,
|
||||
if (misc_seek(mfd->fd, blk * info->phy_block_size))
|
||||
goto out_free_stage2_list;
|
||||
if (install_fba_stage1b(mfd, &stage1b_list, &stage1b_count,
|
||||
stage2_list, stage2_count, info))
|
||||
stage2_list, stage2_count, fs_block_size, info))
|
||||
goto out_free_stage2_list;
|
||||
/* Install stage 0/1 fill in dump partition parameter */
|
||||
if (boot_init_fba_stage0(&stage0, stage1b_list, stage1b_count))
|
||||
@@ -1109,7 +1123,8 @@ install_dump(const char *device, struct job_target_data *target, uint64_t mem,
|
||||
{
|
||||
struct stage2dump_parm_tail stage2dump_parms = {0};
|
||||
struct misc_fd mfd = {0};
|
||||
struct disk_info* info;
|
||||
struct device_info *dev_info;
|
||||
struct disk_info *info;
|
||||
uint64_t part_size;
|
||||
char *tempdev;
|
||||
int rc;
|
||||
@@ -1139,7 +1154,7 @@ install_dump(const char *device, struct job_target_data *target, uint64_t mem,
|
||||
error_text("Could not install dump record on tape "
|
||||
"device '%s'", device);
|
||||
} else {
|
||||
if (verbose) {
|
||||
if (!misc_fsync(&mfd, device) && verbose) {
|
||||
printf("Dump record successfully installed on "
|
||||
"tape device '%s'.\n", device);
|
||||
}
|
||||
@@ -1149,32 +1164,33 @@ install_dump(const char *device, struct job_target_data *target, uint64_t mem,
|
||||
}
|
||||
close(mfd.fd);
|
||||
/* This is a disk device */
|
||||
rc = disk_get_info(device, target, &info);
|
||||
rc = device_get_info(device, target, &dev_info);
|
||||
if (rc) {
|
||||
error_text("Could not get information for dump target "
|
||||
"'%s'", device);
|
||||
return rc;
|
||||
}
|
||||
info = &dev_info->base[0];
|
||||
if (info->partnum == 0) {
|
||||
error_reason("Dump target '%s' is not a disk partition",
|
||||
device);
|
||||
disk_free_info(info);
|
||||
device_free_info(dev_info);
|
||||
return -1;
|
||||
}
|
||||
if (verbose) {
|
||||
printf("Target device information\n");
|
||||
disk_print_info(info, target->source);
|
||||
device_print_info(dev_info, target);
|
||||
}
|
||||
rc = misc_temp_dev(info->device, 1, &tempdev);
|
||||
rc = misc_temp_dev(dev_info->device, 1, &tempdev);
|
||||
if (rc) {
|
||||
disk_free_info(info);
|
||||
device_free_info(dev_info);
|
||||
return -1;
|
||||
}
|
||||
if (misc_open_device(tempdev, &mfd, dry_run) == -1) {
|
||||
error_text("Could not open temporary device node '%s'",
|
||||
tempdev);
|
||||
misc_free_temp_dev(tempdev);
|
||||
disk_free_info(info);
|
||||
device_free_info(dev_info);
|
||||
return -1;
|
||||
}
|
||||
switch (info->type) {
|
||||
@@ -1201,11 +1217,16 @@ install_dump(const char *device, struct job_target_data *target, uint64_t mem,
|
||||
disk_get_type_name(info->type));
|
||||
}
|
||||
if (info->type == disk_type_eckd_ldl)
|
||||
rc = install_svdump_eckd_ldl(&mfd, info, &stage2dump_parms);
|
||||
rc = install_svdump_eckd_ldl(&mfd,
|
||||
dev_info->fs_block_size,
|
||||
info, &stage2dump_parms);
|
||||
else if (info->type == disk_type_eckd_cdl)
|
||||
rc = install_svdump_eckd_cdl(&mfd, info, &stage2dump_parms);
|
||||
rc = install_svdump_eckd_cdl(&mfd,
|
||||
dev_info->fs_block_size,
|
||||
info, &stage2dump_parms);
|
||||
else
|
||||
rc = install_svdump_fba(&mfd, info, &stage2dump_parms);
|
||||
rc = install_svdump_fba(&mfd, dev_info->fs_block_size,
|
||||
info, &stage2dump_parms);
|
||||
break;
|
||||
case disk_type_scsi:
|
||||
error_reason("%s: Unsupported disk type '%s' (try --dumptofs)",
|
||||
@@ -1219,7 +1240,7 @@ install_dump(const char *device, struct job_target_data *target, uint64_t mem,
|
||||
break;
|
||||
}
|
||||
misc_free_temp_dev(tempdev);
|
||||
disk_free_info(info);
|
||||
device_free_info(dev_info);
|
||||
if (fsync(mfd.fd))
|
||||
error_text("Could not sync device file '%s'", device);
|
||||
if (close(mfd.fd))
|
||||
@@ -1232,8 +1253,9 @@ int
|
||||
install_mvdump(char* const device[], struct job_target_data* target, int count,
|
||||
uint64_t mem, uint8_t force)
|
||||
{
|
||||
struct disk_info *info[MAX_DUMP_VOLUMES] = {0};
|
||||
struct device_info *dev_info[MAX_DUMP_VOLUMES] = {0};
|
||||
struct stage2dump_parm_tail stage2dump_parms = {0};
|
||||
struct disk_info *info[MAX_DUMP_VOLUMES] = {0};
|
||||
struct mvdump_parm_table mvdump_parms;
|
||||
uint64_t total_size = 0;
|
||||
struct timeval time;
|
||||
@@ -1267,12 +1289,13 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
|
||||
}
|
||||
close(mfd.fd);
|
||||
/* This is a disk device */
|
||||
rc = disk_get_info(device[i], target, &info[i]);
|
||||
rc = device_get_info(device[i], target, &dev_info[i]);
|
||||
if (rc) {
|
||||
error_text("Could not get information for dump target "
|
||||
"'%s'", device[i]);
|
||||
goto out;
|
||||
}
|
||||
info[i] = &dev_info[i]->base[0];
|
||||
if (info[i]->partnum == 0) {
|
||||
error_reason("Dump target '%s' is not a disk partition",
|
||||
device[i]);
|
||||
@@ -1321,7 +1344,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
|
||||
if (verbose) {
|
||||
for (i = 0; i < count; i++) {
|
||||
printf("Multi-volume dump target %d:\n", i + 1);
|
||||
disk_print_info(info[i], target->source);
|
||||
device_print_info(dev_info[i], target);
|
||||
printf("-------------------------------------------\n");
|
||||
}
|
||||
}
|
||||
@@ -1343,7 +1366,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
|
||||
for (i = 0; i < count; i++) {
|
||||
struct misc_fd mfd = {0};
|
||||
|
||||
rc = misc_temp_dev(info[i]->device, 1, &tempdev);
|
||||
rc = misc_temp_dev(dev_info[i]->device, 1, &tempdev);
|
||||
if (rc) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
@@ -1358,7 +1381,9 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
|
||||
if (verbose)
|
||||
printf("Installing dump record on target partition "
|
||||
"'%s'\n", device[i]);
|
||||
rc = install_mvdump_eckd_cdl(&mfd, info[i], &stage2dump_parms, &mvdump_parms);
|
||||
rc = install_mvdump_eckd_cdl(&mfd, dev_info[i]->fs_block_size,
|
||||
info[i], &stage2dump_parms,
|
||||
&mvdump_parms);
|
||||
misc_free_temp_dev(tempdev);
|
||||
|
||||
if (fsync(mfd.fd))
|
||||
@@ -1372,6 +1397,6 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
|
||||
out:
|
||||
for (i = 0; i < count; i++)
|
||||
if (info[i] != NULL)
|
||||
disk_free_info(info[i]);
|
||||
device_free_info(dev_info[i]);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -131,6 +131,16 @@ int misc_open_device(const char *filename, struct misc_fd *mfd, int simulate)
|
||||
return mfd->fd;
|
||||
}
|
||||
|
||||
int misc_fsync(struct misc_fd *mfd, const char *filename)
|
||||
{
|
||||
if (fsync(mfd->fd)) {
|
||||
error_reason(strerror(errno));
|
||||
error_text("Could not sync file '%s'", filename);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read COUNT bytes of data from file identified by file descriptor FD to
|
||||
* memory at location BUFFER. Return 0 when all bytes were successfully read,
|
||||
* non-zero otherwise. */
|
||||
@@ -434,7 +444,7 @@ misc_write(int fd, const void* data, size_t count)
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of writing, upadte only current position in the file
|
||||
* Instead of writing, update only current position in the file
|
||||
*/
|
||||
static int misc_simulate_write(int fd, size_t count)
|
||||
{
|
||||
|
||||
@@ -1200,7 +1200,7 @@ scan_check_target_data(char* keyword[], int* line)
|
||||
return -1;
|
||||
case target_type_scsi:
|
||||
case target_type_fba:
|
||||
if ((keyword[(int) scan_keyword_targetgeometry] == 0))
|
||||
if (keyword[(int) scan_keyword_targetgeometry] == 0)
|
||||
break;
|
||||
if (line != NULL)
|
||||
error_reason("Line %d: keyword "
|
||||
|
||||
@@ -174,7 +174,8 @@ main(int argc, char* argv[])
|
||||
/* Do it */
|
||||
switch (job->id) {
|
||||
case job_dump_partition:
|
||||
rc = disk_get_ext_type(job->data.dump.device, &ext_type);
|
||||
rc = disk_get_ext_type(job->data.dump.device, &ext_type,
|
||||
0 /* disk index */);
|
||||
if (rc)
|
||||
break;
|
||||
job_dump_check_set_ngdump(job, &ext_type);
|
||||
@@ -210,16 +211,16 @@ main(int argc, char* argv[])
|
||||
case job_menu:
|
||||
rc = prepare_bootloader(job, &bis);
|
||||
if (rc) {
|
||||
free_bootloader(&bis);
|
||||
free_bootloader(&bis, job);
|
||||
break;
|
||||
}
|
||||
rc = install_bootloader(job, &bis);
|
||||
if (rc) {
|
||||
free_bootloader(&bis);
|
||||
free_bootloader(&bis, job);
|
||||
break;
|
||||
}
|
||||
rc = post_install_bootloader(job, &bis);
|
||||
free_bootloader(&bis);
|
||||
free_bootloader(&bis, job);
|
||||
break;
|
||||
case job_ipl_tape:
|
||||
rc = install_tapeloader(job->data.ipl_tape.device,
|
||||
@@ -244,10 +245,6 @@ main(int argc, char* argv[])
|
||||
}
|
||||
switch (rc) {
|
||||
case 0: /* Operation completed successfully */
|
||||
if (verbose)
|
||||
printf("Syncing disks...\n");
|
||||
if (!dry_run)
|
||||
sync();
|
||||
printf("Done.\n");
|
||||
break;
|
||||
case -2: /* Operation canceled by user */
|
||||
@@ -306,7 +303,8 @@ struct component_footer component_footers[NR_PROGRAM_COMPONENTS] = {
|
||||
},
|
||||
[COMPONENT_ID_ENVBLK] = {
|
||||
.type = COMPONENT_TYPE_LOAD,
|
||||
.desc = "environment blk"
|
||||
.desc = "environment blk",
|
||||
.fs_block_aligned = 1
|
||||
},
|
||||
[COMPONENT_ID_SEGMENT_FILE] = {
|
||||
.type = COMPONENT_TYPE_EXECUTE,
|
||||
|
||||
@@ -1045,6 +1045,8 @@ static int get_table(dev_t dev, struct util_list **table)
|
||||
&start, &length, &type, &args) < 4) {
|
||||
ERR("Unrecognized device-mapper table format for device '%s'\n",
|
||||
devname);
|
||||
free(type);
|
||||
free(args);
|
||||
goto out;
|
||||
}
|
||||
tops = find_target_ops(type);
|
||||
@@ -1052,6 +1054,9 @@ static int get_table(dev_t dev, struct util_list **table)
|
||||
ERR("Unsupported setup: Unsupported device-mapper "
|
||||
"target type '%s' for device '%s'\n",
|
||||
type, devname);
|
||||
free(type);
|
||||
free(args);
|
||||
goto out;
|
||||
}
|
||||
data = tops->get_target_data(devname, args);
|
||||
free(type);
|
||||
|
||||
@@ -3292,8 +3292,9 @@ int kms_configure(const kms_handle_t handle,
|
||||
opts.cert_digest,
|
||||
#ifdef EKMF_SUPPORTS_RSA_PSS_CERTIFICATES
|
||||
opts.cert_rsa_pss,
|
||||
#endif
|
||||
#else
|
||||
false);
|
||||
#endif
|
||||
else
|
||||
rc = _error_gen_csr_sscert_opts(ph, &opts);
|
||||
if (rc != 0)
|
||||
@@ -3937,7 +3938,7 @@ static int _restrict_key(struct plugin_handle *ph, unsigned char *key_blob,
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = check_aes_cipher_key(key_blob, key_blob_length);
|
||||
rc = check_aes_cipher_key(key_blob, key_blob_length, false);
|
||||
if (rc != 0) {
|
||||
warnx("The secure key retrieved from EKMF Web might not be "
|
||||
"secure");
|
||||
|
||||
@@ -121,6 +121,12 @@ typedef struct {
|
||||
|
||||
#define CKR_OK 0x00000000
|
||||
|
||||
#define XCP_BLOB_EXTRACTABLE 0x00000001
|
||||
#define XCP_BLOB_DECRYPT 0x00000400
|
||||
#define XCP_BLOB_ENCRYPT 0x00000800
|
||||
#define XCP_BLOB_WRAP_W_TRUSTED 0x00040000
|
||||
#define XCP_BLOB_PROTKEY_EXTRACTABLE 0x00200000
|
||||
|
||||
typedef int (*m_init_t) (void);
|
||||
typedef int (*m_add_module_t) (XCP_Module_t module, target_t *target);
|
||||
typedef int (*m_rm_module_t) (XCP_Module_t module, target_t target);
|
||||
|
||||
@@ -1983,6 +1983,9 @@ out:
|
||||
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
|
||||
* @param[in] passphrase_file the file name of a file containing a passphrase
|
||||
* for LUKS2 (optional, can be NULL)
|
||||
* @param[in] exportable if true the key shall be exportable
|
||||
* @param[in] wrap_with_trusted if true the key shall be wrapable with a
|
||||
* trusted KEK only
|
||||
* @param[in] pkey_fd the file descriptor of /dev/pkey
|
||||
*
|
||||
* @returns 0 for success or a negative errno in case of an error
|
||||
@@ -1993,7 +1996,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
|
||||
size_t sector_size, size_t keybits, bool xts,
|
||||
const char *clear_key_file, const char *volume_type,
|
||||
const char *key_type, bool gen_passphrase,
|
||||
const char *passphrase_file, int pkey_fd)
|
||||
const char *passphrase_file, bool exportable,
|
||||
bool wrap_with_trusted, int pkey_fd)
|
||||
{
|
||||
struct key_filenames file_names = { 0 };
|
||||
struct properties *key_props = NULL;
|
||||
@@ -2043,6 +2047,7 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
|
||||
file_names.skey_filename,
|
||||
keybits, xts, key_type,
|
||||
(const char **)apqn_list,
|
||||
exportable, wrap_with_trusted,
|
||||
keystore->verbose);
|
||||
else
|
||||
rc = generate_secure_key_clear(pkey_fd,
|
||||
@@ -2050,6 +2055,7 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
|
||||
keybits, xts, clear_key_file,
|
||||
key_type,
|
||||
(const char **)apqn_list,
|
||||
exportable, wrap_with_trusted,
|
||||
keystore->verbose);
|
||||
if (rc != 0)
|
||||
goto out_free_props;
|
||||
@@ -2286,6 +2292,7 @@ out_free_key_filenames:
|
||||
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
|
||||
* @param[in] passphrase_file the file name of a file containing a passphrase
|
||||
* for LUKS2 (optional, can be NULL)
|
||||
* @param[in] exportable if true the key shall be exportable
|
||||
* @param[in] lib the external library struct
|
||||
*
|
||||
* @returns 0 for success or a negative errno in case of an error
|
||||
@@ -2295,7 +2302,8 @@ int keystore_import(struct keystore *keystore, unsigned char *secure_key,
|
||||
const char *description, const char *volumes,
|
||||
const char *apqns, bool noapqncheck, size_t sector_size,
|
||||
const char *volume_type, bool gen_passphrase,
|
||||
const char *passphrase_file, struct ext_lib *lib)
|
||||
const char *passphrase_file, bool exportable,
|
||||
struct ext_lib *lib)
|
||||
{
|
||||
struct key_filenames file_names = { 0 };
|
||||
struct properties *key_props = NULL;
|
||||
@@ -2375,17 +2383,22 @@ int keystore_import(struct keystore *keystore, unsigned char *secure_key,
|
||||
goto out_free_props;
|
||||
}
|
||||
|
||||
rc = restrict_key_export(lib->cca, secure_key, secure_key_size,
|
||||
keystore->verbose);
|
||||
if (rc != 0) {
|
||||
warnx("Failed to export-restrict the imported secure "
|
||||
"key: %s", strerror(-rc));
|
||||
if (!selected)
|
||||
print_msg_for_cca_envvars("secure AES key");
|
||||
goto out_free_props;
|
||||
if (!exportable) {
|
||||
rc = restrict_key_export(lib->cca, secure_key,
|
||||
secure_key_size,
|
||||
keystore->verbose);
|
||||
if (rc != 0) {
|
||||
warnx("Failed to export-restrict the imported "
|
||||
"secure key: %s", strerror(-rc));
|
||||
if (!selected)
|
||||
print_msg_for_cca_envvars("secure AES "
|
||||
"key");
|
||||
goto out_free_props;
|
||||
}
|
||||
}
|
||||
|
||||
rc = check_aes_cipher_key(secure_key, secure_key_size);
|
||||
rc = check_aes_cipher_key(secure_key, secure_key_size,
|
||||
exportable);
|
||||
if (rc != 0) {
|
||||
warnx("The secure key to import might not be secure");
|
||||
printf("%s: Do you want to import it anyway [y/N]? ",
|
||||
@@ -2455,6 +2468,7 @@ out_free_key_filenames:
|
||||
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
|
||||
* @param[in] passphrase_file the file name of a file containing a passphrase
|
||||
* for LUKS2 (optional, can be NULL)
|
||||
* @param[in] exportable if true the key shall be exportable
|
||||
* @param[in] lib the external library struct
|
||||
*
|
||||
* @returns 0 for success or a negative errno in case of an error
|
||||
@@ -2464,7 +2478,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
|
||||
const char *apqns, bool noapqncheck, size_t sector_size,
|
||||
const char *import_file, const char *volume_type,
|
||||
bool gen_passphrase, const char *passphrase_file,
|
||||
struct ext_lib *lib)
|
||||
bool exportable, struct ext_lib *lib)
|
||||
{
|
||||
size_t secure_key_size;
|
||||
u8 *secure_key;
|
||||
@@ -2480,7 +2494,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
|
||||
rc = keystore_import(keystore, secure_key, secure_key_size, name,
|
||||
description, volumes, apqns, noapqncheck,
|
||||
sector_size, volume_type, gen_passphrase,
|
||||
passphrase_file, lib);
|
||||
passphrase_file, exportable, lib);
|
||||
|
||||
if (secure_key != NULL)
|
||||
free(secure_key);
|
||||
@@ -4797,6 +4811,7 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
|
||||
* @param[in] key_type the type of the key to convert it to
|
||||
* @param[in] noapqncheck if true, the specified APQN(s) are not checked for
|
||||
* existence and type.
|
||||
* @param[in] exportable if true the key shall be exportable
|
||||
* @param[in] pkey_fd the file descriptor of /dev/pkey
|
||||
* @param[in] lib the external library struct
|
||||
*
|
||||
@@ -4804,7 +4819,7 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
|
||||
*/
|
||||
int keystore_convert_key(struct keystore *keystore, const char *name,
|
||||
const char *key_type, bool noapqncheck, bool quiet,
|
||||
int pkey_fd, struct ext_lib *lib)
|
||||
bool exportable, int pkey_fd, struct ext_lib *lib)
|
||||
{
|
||||
struct key_filenames file_names = { 0 };
|
||||
u8 output_key[2 * MAX_SECURE_KEY_SIZE];
|
||||
@@ -4941,14 +4956,16 @@ int keystore_convert_key(struct keystore *keystore, const char *name,
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = restrict_key_export(lib->cca, output_key, output_key_size,
|
||||
keystore->verbose);
|
||||
if (rc != 0) {
|
||||
warnx("Export restricting the converted secure key '%s' has "
|
||||
"failed", name);
|
||||
if (!selected)
|
||||
print_msg_for_cca_envvars("secure AES key");
|
||||
goto out;
|
||||
if (!exportable) {
|
||||
rc = restrict_key_export(lib->cca, output_key, output_key_size,
|
||||
keystore->verbose);
|
||||
if (rc != 0) {
|
||||
warnx("Export restricting the converted secure key "
|
||||
"'%s' has failed", name);
|
||||
if (!selected)
|
||||
print_msg_for_cca_envvars("secure AES key");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rc = properties_set2(properties, PROP_NAME_KEY_TYPE, key_type, true);
|
||||
|
||||
@@ -55,7 +55,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
|
||||
size_t sector_size, size_t keybits, bool xts,
|
||||
const char *clear_key_file, const char *volume_type,
|
||||
const char *key_type, bool gen_passphrase,
|
||||
const char *passphrase_file, int pkey_fd);
|
||||
const char *passphrase_file, bool exportable,
|
||||
bool wrap_with_trusted, int pkey_fd);
|
||||
|
||||
int keystore_generate_key_kms(struct keystore *keystore, const char *name,
|
||||
const char *description, const char *volumes,
|
||||
@@ -70,14 +71,15 @@ int keystore_import(struct keystore *keystore, unsigned char *secure_key,
|
||||
const char *description, const char *volumes,
|
||||
const char *apqns, bool noapqncheck, size_t sector_size,
|
||||
const char *volume_type, bool gen_passphrase,
|
||||
const char *passphrase_file, struct ext_lib *lib);
|
||||
const char *passphrase_file, bool exportable,
|
||||
struct ext_lib *lib);
|
||||
|
||||
int keystore_import_key(struct keystore *keystore, const char *name,
|
||||
const char *description, const char *volumes,
|
||||
const char *apqns, bool noapqncheck, size_t sector_size,
|
||||
const char *import_file, const char *volume_type,
|
||||
bool gen_passphrase, const char *passphrase_file,
|
||||
struct ext_lib *lib);
|
||||
bool exportable, struct ext_lib *lib);
|
||||
|
||||
int keystore_change_key(struct keystore *keystore, const char *name,
|
||||
const char *description, const char *volumes,
|
||||
@@ -126,7 +128,7 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter,
|
||||
|
||||
int keystore_convert_key(struct keystore *keystore, const char *name,
|
||||
const char *key_type, bool noapqncheck, bool quiet,
|
||||
int pkey_fd, struct ext_lib *lib);
|
||||
bool exportable, int pkey_fd, struct ext_lib *lib);
|
||||
|
||||
int keystore_kms_keys_set_property(struct keystore *keystore,
|
||||
const char *key_type,
|
||||
|
||||
@@ -745,7 +745,7 @@ static int remove_directory_recursively(const char *directory)
|
||||
|
||||
if (rmdir(directory) != 0) {
|
||||
rc = -errno;
|
||||
warnx("Failed to remove '%s': %s", filename, strerror(-rc));
|
||||
warnx("Failed to remove '%s': %s", directory, strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -2448,7 +2448,7 @@ int set_kms_key_properties(struct kms_info *kms_info,
|
||||
ADD_KMS_PROPS(kms_props, num_kms_props,
|
||||
KMS_KEY_PROP_SECTOR_SIZE, sector_size);
|
||||
if (passphrase_file != NULL) {
|
||||
/* *passphrase_file is NULL to remove the propoerty */
|
||||
/* *passphrase_file is NULL to remove the property */
|
||||
if (*passphrase_file != NULL) {
|
||||
passphrase =
|
||||
read_passphrase_as_base64(*passphrase_file,
|
||||
@@ -2797,7 +2797,7 @@ static int _process_kms_keys_cb(const char *key_id, const char *key_label,
|
||||
* @param[in] name_filter the name filter. Can contain wild cards.
|
||||
* NULL means no name filter.
|
||||
* @param[in] volume_filter the volume filter. Can contain wild cards, and
|
||||
* mutliple volume filters separated by commas.
|
||||
* multiple volume filters separated by commas.
|
||||
* If the filter does not contain the ':dm-name'
|
||||
* part, then the volumes are matched without the
|
||||
* dm-name part. If the filter contains the
|
||||
@@ -2989,7 +2989,7 @@ static int _list_kms_keys_cb(const char *UNUSED(key1_id),
|
||||
* @param[in] name_filter the name filter. Can contain wild cards.
|
||||
* NULL means no name filter.
|
||||
* @param[in] volume_filter the volume filter. Can contain wild cards, and
|
||||
* mutliple volume filters separated by commas.
|
||||
* multiple volume filters separated by commas.
|
||||
* If the filter does not contain the ':dm-name'
|
||||
* part, then the volumes are matched without the
|
||||
* dm-name part. If the filter contains the
|
||||
|
||||
96
zkey/pkey.c
96
zkey/pkey.c
@@ -313,6 +313,35 @@ static enum pkey_key_size keybits_to_keysize(u32 keybits)
|
||||
}
|
||||
}
|
||||
|
||||
static u32 flags_for_pkey_type(enum pkey_key_type type, bool exportable,
|
||||
bool wrap_with_trusted)
|
||||
{
|
||||
u32 flags = 0;
|
||||
|
||||
if (!exportable)
|
||||
return 0;
|
||||
|
||||
switch (type) {
|
||||
case PKEY_TYPE_CCA_CIPHER:
|
||||
flags = PKEY_KEYGEN_XPRT_SYM | PKEY_KEYGEN_XPRT_UASY |
|
||||
PKEY_KEYGEN_XPRT_AASY | PKEY_KEYGEN_XPRT_RAW |
|
||||
PKEY_KEYGEN_XPRT_CPAC | PKEY_KEYGEN_XPRT_DES |
|
||||
PKEY_KEYGEN_XPRT_AES | PKEY_KEYGEN_XPRT_RSA;
|
||||
break;
|
||||
case PKEY_TYPE_EP11:
|
||||
case PKEY_TYPE_EP11_AES:
|
||||
flags = XCP_BLOB_EXTRACTABLE | XCP_BLOB_DECRYPT |
|
||||
XCP_BLOB_ENCRYPT | XCP_BLOB_PROTKEY_EXTRACTABLE;
|
||||
if (wrap_with_trusted)
|
||||
flags |= XCP_BLOB_WRAP_W_TRUSTED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper for the PKEY_GENSECK/PKEY_GENSECK2 IOCTL to generate a secure
|
||||
* key of any type by random. If the newer PKEY_GENSECK2 IOCTL is not supported
|
||||
@@ -887,6 +916,16 @@ static size_t key_size_for_type(enum pkey_key_type type)
|
||||
}
|
||||
}
|
||||
|
||||
static void print_error_for_ep11_exportable(void)
|
||||
{
|
||||
util_print_indented("Generating an exportable key of type "
|
||||
KEY_TYPE_EP11_AES " requires that access control "
|
||||
"point (ACP) 'XCP_CPB_ALLOW_COMBINED_EXTRACT' is "
|
||||
"'ON' on all used APQNs. This access control point "
|
||||
"is only supported on newer EP11 firmware levels.",
|
||||
0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a secure key by random
|
||||
*
|
||||
@@ -897,13 +936,17 @@ static size_t key_size_for_type(enum pkey_key_type type)
|
||||
* @param[in] key_type the type of the key
|
||||
* @param[in] apqns a zero terminated array of pointers to APQN-strings,
|
||||
* or NULL for AUTOSELECT
|
||||
* @param[in] exportable if true the key shall be exportable
|
||||
* @param[in] wrap_with_trusted if true the key shall be wrapable with a
|
||||
* trusted KEK only
|
||||
* @param[in] verbose if true, verbose messages are printed
|
||||
*
|
||||
* @returns 0 on success, a negative errno in case of an error
|
||||
*/
|
||||
int generate_secure_key_random(int pkey_fd, const char *keyfile,
|
||||
size_t keybits, bool xts, const char *key_type,
|
||||
const char **apqns, bool verbose)
|
||||
const char **apqns, bool exportable,
|
||||
bool wrap_with_trusted, bool verbose)
|
||||
{
|
||||
struct pkey_genseck2 genseck2;
|
||||
size_t secure_key_size, size;
|
||||
@@ -960,6 +1003,9 @@ retry:
|
||||
genseck2.key = secure_key;
|
||||
genseck2.keylen = size;
|
||||
|
||||
genseck2.keygenflags = flags_for_pkey_type(genseck2.type, exportable,
|
||||
wrap_with_trusted);
|
||||
|
||||
rc = pkey_genseck2(pkey_fd, &genseck2, verbose);
|
||||
if (rc == -EINVAL && genseck2.type == PKEY_TYPE_EP11_AES) {
|
||||
/*
|
||||
@@ -979,6 +1025,9 @@ retry:
|
||||
}
|
||||
if (rc != 0) {
|
||||
warnx("Failed to generate a secure key: %s", strerror(-rc));
|
||||
if (exportable &&
|
||||
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
||||
print_error_for_ep11_exportable();
|
||||
goto out;
|
||||
}
|
||||
if (rc == 0 && genseck2.type == PKEY_TYPE_EP11) {
|
||||
@@ -1016,10 +1065,17 @@ retry:
|
||||
genseck2.key = secure_key + size;
|
||||
genseck2.keylen = size;
|
||||
|
||||
genseck2.keygenflags = flags_for_pkey_type(genseck2.type,
|
||||
exportable,
|
||||
wrap_with_trusted);
|
||||
|
||||
rc = pkey_genseck2(pkey_fd, &genseck2, verbose);
|
||||
if (rc != 0) {
|
||||
warnx("Failed to generate a secure key: %s",
|
||||
strerror(-rc));
|
||||
if (exportable &&
|
||||
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
||||
print_error_for_ep11_exportable();
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -1048,6 +1104,9 @@ out:
|
||||
* @param[in] key_type the type of the key
|
||||
* @param[in] apqns a zero terminated array of pointers to APQN-strings,
|
||||
* or NULL for AUTOSELECT
|
||||
* @param[in] exportable if true the key shall be exportable
|
||||
* @param[in] wrap_with_trusted if true the key shall be wrapable with a
|
||||
* trusted KEK only
|
||||
* @param[in] verbose if true, verbose messages are printed
|
||||
*
|
||||
* @returns 0 on success, a negative errno in case of an error
|
||||
@@ -1055,7 +1114,8 @@ out:
|
||||
int generate_secure_key_clear(int pkey_fd, const char *keyfile,
|
||||
size_t keybits, bool xts,
|
||||
const char *clearkeyfile, const char *key_type,
|
||||
const char **apqns, bool verbose)
|
||||
const char **apqns, bool exportable,
|
||||
bool wrap_with_trusted, bool verbose)
|
||||
{
|
||||
struct pkey_clr2seck2 clr2seck2;
|
||||
size_t secure_key_size;
|
||||
@@ -1123,6 +1183,9 @@ retry:
|
||||
clr2seck2.key = secure_key;
|
||||
clr2seck2.keylen = size;
|
||||
|
||||
clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type, exportable,
|
||||
wrap_with_trusted);
|
||||
|
||||
rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose);
|
||||
if (rc == -EINVAL && clr2seck2.type == PKEY_TYPE_EP11_AES) {
|
||||
/*
|
||||
@@ -1142,6 +1205,9 @@ retry:
|
||||
}
|
||||
if (rc != 0) {
|
||||
warnx("Failed to generate a secure key: %s", strerror(-rc));
|
||||
if (exportable &&
|
||||
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
||||
print_error_for_ep11_exportable();
|
||||
goto out;
|
||||
}
|
||||
if (rc == 0 && clr2seck2.type == PKEY_TYPE_EP11) {
|
||||
@@ -1182,10 +1248,17 @@ retry:
|
||||
clr2seck2.key = secure_key + size;
|
||||
clr2seck2.keylen = size;
|
||||
|
||||
clr2seck2.keygenflags = flags_for_pkey_type(clr2seck2.type,
|
||||
exportable,
|
||||
wrap_with_trusted);
|
||||
|
||||
rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose);
|
||||
if (rc != 0) {
|
||||
warnx("Failed to generate a secure key: %s",
|
||||
strerror(-rc));
|
||||
if (exportable &&
|
||||
strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
|
||||
print_error_for_ep11_exportable();
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -2044,10 +2117,11 @@ enum card_type get_card_type_for_keytype(const char *key_type)
|
||||
*
|
||||
* @param[in] key the secure key token
|
||||
* @param[in] key_size the size of the secure key
|
||||
* @param[in] exportable if true the key shall be exportable
|
||||
*
|
||||
* @returns 0 on success, a negative errno in case of an error
|
||||
*/
|
||||
int check_aes_cipher_key(const u8 *key, size_t key_size)
|
||||
int check_aes_cipher_key(const u8 *key, size_t key_size, bool exportable)
|
||||
{
|
||||
struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
|
||||
bool mismatch = false;
|
||||
@@ -2072,22 +2146,22 @@ int check_aes_cipher_key(const u8 *key, size_t key_size)
|
||||
mismatch = true;
|
||||
}
|
||||
|
||||
if (cipherkey->kmf1 & 0x8000) {
|
||||
if (!exportable && (cipherkey->kmf1 & 0x8000)) {
|
||||
printf("WARNING: The secure key can be exported using a "
|
||||
"symmetric key\n");
|
||||
mismatch = true;
|
||||
}
|
||||
if (cipherkey->kmf1 & 0x4000) {
|
||||
if (!exportable && (cipherkey->kmf1 & 0x4000)) {
|
||||
printf("WARNING: The secure key can be exported using an "
|
||||
"unauthenticated asymmetric key\n");
|
||||
mismatch = true;
|
||||
}
|
||||
if (cipherkey->kmf1 & 0x2000) {
|
||||
if (!exportable && (cipherkey->kmf1 & 0x2000)) {
|
||||
printf("WARNING: The secure key can be exported using an "
|
||||
"authenticated asymmetric key\n");
|
||||
mismatch = true;
|
||||
}
|
||||
if (cipherkey->kmf1 & 0x1000) {
|
||||
if (!exportable && (cipherkey->kmf1 & 0x1000)) {
|
||||
printf("WARNING: The secure key can be exported using a RAW "
|
||||
"key\n");
|
||||
mismatch = true;
|
||||
@@ -2097,17 +2171,17 @@ int check_aes_cipher_key(const u8 *key, size_t key_size)
|
||||
"CPACF protected key\n");
|
||||
mismatch = true;
|
||||
}
|
||||
if ((cipherkey->kmf1 & 0x0080) == 0) {
|
||||
if (!exportable && (cipherkey->kmf1 & 0x0080) == 0) {
|
||||
printf("WARNING: The secure key can be exported using a DES "
|
||||
"key\n");
|
||||
mismatch = true;
|
||||
}
|
||||
if ((cipherkey->kmf1 & 0x0040) == 0) {
|
||||
if (!exportable && (cipherkey->kmf1 & 0x0040) == 0) {
|
||||
printf("WARNING: The secure key can be exported using an AES "
|
||||
"key\n");
|
||||
mismatch = true;
|
||||
}
|
||||
if ((cipherkey->kmf1 & 0x0008) == 0) {
|
||||
if (!exportable && (cipherkey->kmf1 & 0x0008) == 0) {
|
||||
printf("WARNING: The secure key can be exported using an RSA "
|
||||
"key\n");
|
||||
mismatch = true;
|
||||
@@ -2161,7 +2235,7 @@ int check_aes_cipher_key(const u8 *key, size_t key_size)
|
||||
"value\n");
|
||||
mismatch = true;
|
||||
}
|
||||
if ((cipherkey->kmf3 & 0x00FF) == 0x0012) {
|
||||
if (!exportable && (cipherkey->kmf3 & 0x00FF) == 0x0012) {
|
||||
printf("WARNING: The secure key was converted from a CCA "
|
||||
"key-token that had no export control attributes\n");
|
||||
mismatch = true;
|
||||
|
||||
@@ -330,12 +330,14 @@ int open_pkey_device(bool verbose);
|
||||
|
||||
int generate_secure_key_random(int pkey_fd, const char *keyfile,
|
||||
size_t keybits, bool xts, const char *key_type,
|
||||
const char **apqns, bool verbose);
|
||||
const char **apqns, bool exportable,
|
||||
bool wrap_with_trusted, bool verbose);
|
||||
|
||||
int generate_secure_key_clear(int pkey_fd, const char *keyfile,
|
||||
size_t keybits, bool xts,
|
||||
const char *clearkeyfile, const char *key_type,
|
||||
const char **apqns, bool verbose);
|
||||
const char **apqns, bool exportable,
|
||||
bool wrap_with_trusted, bool verbose);
|
||||
|
||||
u8 *read_secure_key(const char *keyfile, size_t *secure_key_size,
|
||||
bool verbose);
|
||||
@@ -373,7 +375,7 @@ bool is_secure_key_type(const char *key_type);
|
||||
int get_min_card_level_for_keytype(const char *key_type);
|
||||
const struct fw_version *get_min_fw_version_for_keytype(const char *key_type);
|
||||
enum card_type get_card_type_for_keytype(const char *key_type);
|
||||
int check_aes_cipher_key(const u8 *key, size_t key_size);
|
||||
int check_aes_cipher_key(const u8 *key, size_t key_size, bool exportable);
|
||||
|
||||
enum reencipher_method {
|
||||
REENCIPHER_OLD_TO_CURRENT = 1,
|
||||
|
||||
@@ -674,7 +674,7 @@ int pvsecrets_import(struct keystore *keystore, int uv_fd,
|
||||
rc = keystore_import(keystore, (unsigned char *)&build_blob_data.token,
|
||||
sizeof(build_blob_data.token), name, description,
|
||||
volumes, NULL, false, sector_size, volume_type,
|
||||
gen_passphrase, passphrase_file, NULL);
|
||||
gen_passphrase, passphrase_file, false, NULL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
43
zkey/zkey.1
43
zkey/zkey.1
@@ -100,6 +100,8 @@ key repository.
|
||||
.IR clear\-key\-file ]
|
||||
.RB [ \-\-key\-type | \-K
|
||||
.IR type ]
|
||||
.RB [ \-\-exportable ]
|
||||
.RB [ \-\-wrap\-with\-trusted ]
|
||||
.RB [ \-\-verbose | \-V ]
|
||||
.
|
||||
.PP
|
||||
@@ -129,6 +131,8 @@ key repository.
|
||||
.RB [ \-\-gen\-dummy\-passphrase ]
|
||||
.RB [ \-\-set\-dummy\-passphrase
|
||||
.IR passphrase\-file ]
|
||||
.RB [ \-\-exportable ]
|
||||
.RB [ \-\-wrap\-with\-trusted ]
|
||||
.RB [ KMS-plugin\ specific\ options ]
|
||||
.RB [ \-\-verbose | \-V ]
|
||||
.PP
|
||||
@@ -183,6 +187,29 @@ Secure keys of type \fBCCA\-AESCIPHER\fP require an IBM cryptographic
|
||||
adapter in CCA coprocessor mode of version 6 or later, e.g. a CEX6C.
|
||||
Secure keys of type \fBEP11\-AES\fP require an IBM cryptographic
|
||||
adapter in EP11 coprocessor mode of version 7 or later, e.g. a CEX7P.
|
||||
.PP
|
||||
By default, secure keys of type \fBCCA\-AESCIPHER\fP and \fBEP11\-AES\fP are
|
||||
export restricted when generated by zkey. Keys of type \fBCCA\-AESDATA\fP can
|
||||
not be export restricted. Export restricted means that the secure keys can not
|
||||
be wrapped by a key encrypting key (KEK), and thus can not escape the
|
||||
protection of the IBM cryptographic adapter (HSM). If exportability is required
|
||||
for a secure key of type \fBCCA\-AESCIPHER\fP or \fBEP11\-AES\fP, specify
|
||||
option \fB\-\-exportable\fP. This makes the generated secure keys exportable.
|
||||
Additionally, for keys of type \fBEP11\-AES\fP option
|
||||
\fB\-\-wrap\-with\-trusted\fP can be specified to allow wrapping only with a
|
||||
trusted KEK. When the secure key repository is bound to a key management system
|
||||
plugin (KMS plugin), options \fB\-\-exportable\fP and
|
||||
\fB\-\-wrap\-with\-trusted\fP are not allowed, except the \fB\-\-local\fP option
|
||||
is specified.
|
||||
|
||||
.PP
|
||||
.B Note:
|
||||
For keys of type \fBEP11\-AES\fP to be exportable, the access control point
|
||||
(ACP) \fBXCP_CPB_ALLOW_COMBINED_EXTRACT\fP must be \fBON\fP on all APQNs used.
|
||||
This access control point is only supported on newer EP11 firmware levels. If
|
||||
the access control point is \fBOFF\fP or not supported by the EP11 firmware,
|
||||
key generation fails with a generic error (Input/output error). The \fBzkey\fP
|
||||
tool prints an appropriate error message in that case.
|
||||
.
|
||||
.SS "Validating secure AES keys"
|
||||
.
|
||||
@@ -391,6 +418,7 @@ EP11\-AES to be installed. For the supported environments and downloads, see:
|
||||
.RB [ \-\-gen\-dummy\-passphrase ]
|
||||
.RB [ \-\-set\-dummy\-passphrase
|
||||
.IR passphrase\-file ]
|
||||
.RB [ \-\-exportable ]
|
||||
.RB [ \-\-verbose | \-V ]
|
||||
.
|
||||
.PP
|
||||
@@ -416,6 +444,13 @@ The \fBimport\fP command requires the CCA host library (libcsulcca.so)
|
||||
to be installed when secure keys of type \fBCCA\-AESCIPHER\fP are imported.
|
||||
For the supported environments and downloads, see:
|
||||
\fIhttp://www.ibm.com/security/cryptocards\fP
|
||||
.PP
|
||||
By default, secure keys of type \fBCCA\-AESCIPHER\fP are changed to be export
|
||||
restricted during the import operation. Export restricted means that the secure
|
||||
keys can not be wrapped by a key encrypting key (KEK), and thus can not escape
|
||||
the protection of the IBM cryptographic adapter (HSM). If exportability of an
|
||||
imported key is required, specify option \fB\-\-exportable\fP. The import
|
||||
operation will then not change the exportability of the secure key.
|
||||
.
|
||||
.SS "Export AES secure keys from the secure key repository"
|
||||
.
|
||||
@@ -798,6 +833,7 @@ same way as with \fBcryptsetup\fP.
|
||||
.IR type
|
||||
.RB [ \-\-no\-apqn\-check ]
|
||||
.RB [ \-\-force | \-F ]
|
||||
.RB [ \-\-exportable ]
|
||||
.RB [ \-\-verbose | \-V ]
|
||||
.
|
||||
.PP
|
||||
@@ -844,6 +880,13 @@ The \fBconvert\fP command requires the CCA host library (libcsulcca.so)
|
||||
to be installed. The required CCA IBM cryptographic adapter firmware version
|
||||
is 6.3.27 or later. For the supported environments and downloads, see:
|
||||
\fIhttp://www.ibm.com/security/cryptocards\fP
|
||||
.PP
|
||||
By default, secure keys are changed to be export restricted during the convert
|
||||
operation. Export restricted means that the secure keys can not be wrapped by a
|
||||
key encrypting key (KEK), and thus can not escape the protection of the IBM
|
||||
cryptographic adapter (HSM). If exportability of a converted key is required,
|
||||
specify option \fB\-\-exportable\fP. The convert operation will then not change
|
||||
the exportability of the secure key.
|
||||
.
|
||||
.
|
||||
.SH COMMANDS FOR KEY MANAGEMENT SYSTEM INTEGRATION
|
||||
|
||||
123
zkey/zkey.c
123
zkey/zkey.c
@@ -83,6 +83,8 @@ static struct zkey_globals {
|
||||
bool gen_passphrase;
|
||||
char *passphrase_file;
|
||||
bool remove_passphrase;
|
||||
bool exportable;
|
||||
bool wrap_with_trusted;
|
||||
bool kms_bound;
|
||||
bool run;
|
||||
bool batch_mode;
|
||||
@@ -173,6 +175,8 @@ static struct zkey_globals {
|
||||
#define OPT_GEN_DUMMY_PASSPHRASE 265
|
||||
#define OPT_SET_DUMMY_PASSPHRASE 266
|
||||
#define OPT_REMOVE_DUMMY_PASSPHRASE 267
|
||||
#define OPT_EXPORTABLE 268
|
||||
#define OPT_WRAP_WITH_TRUSTED 269
|
||||
|
||||
/*
|
||||
* Configuration of command line options
|
||||
@@ -313,6 +317,28 @@ static struct util_opt opt_vec[] = {
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.command = COMMAND_GENERATE,
|
||||
},
|
||||
{
|
||||
.option = {"exportable", 0, NULL, OPT_EXPORTABLE},
|
||||
.desc = "Generate a secure AES key that is not export "
|
||||
"restricted. Exportable keys can be exported by means "
|
||||
"of wrapping them with a key encryption key (KEK). By "
|
||||
"default keys of type " KEY_TYPE_CCA_AESCIPHER " and "
|
||||
KEY_TYPE_EP11_AES " are export restricted, and thus can "
|
||||
"not be wrapped by a KEK. Keys of type "
|
||||
KEY_TYPE_CCA_AESDATA " are always exportable, and can "
|
||||
"not be export restricted.",
|
||||
.command = COMMAND_GENERATE,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
{
|
||||
.option = {"wrap-with-trusted", 0, NULL, OPT_WRAP_WITH_TRUSTED},
|
||||
.desc = "Generate a secure AES key that can only be wrapped "
|
||||
"with a trusted key encryption key (KEK). This option "
|
||||
"is only valid for keys of type " KEY_TYPE_EP11_AES
|
||||
" and only if option '--exportable' is also specified.",
|
||||
.command = COMMAND_GENERATE,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
/***********************************************************/
|
||||
{
|
||||
.flags = UTIL_OPT_FLAG_SECTION,
|
||||
@@ -496,6 +522,19 @@ static struct util_opt opt_vec[] = {
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
.command = COMMAND_IMPORT,
|
||||
},
|
||||
{
|
||||
.option = {"exportable", 0, NULL, OPT_EXPORTABLE},
|
||||
.desc = "Allow to import a secure AES key that is not export "
|
||||
"restricted. Exportable keys can be exported by means "
|
||||
"of wrapping them with a key encryption key (KEK). By "
|
||||
"default keys of type " KEY_TYPE_CCA_AESCIPHER " and "
|
||||
KEY_TYPE_EP11_AES " are export restricted, and thus can "
|
||||
"not be wrapped by a KEK. Keys of type "
|
||||
KEY_TYPE_CCA_AESDATA " are always exportable, and can "
|
||||
"not be export restricted.",
|
||||
.command = COMMAND_IMPORT,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
/***********************************************************/
|
||||
{
|
||||
.flags = UTIL_OPT_FLAG_SECTION,
|
||||
@@ -969,6 +1008,14 @@ static struct util_opt opt_vec[] = {
|
||||
"key",
|
||||
.command = COMMAND_CONVERT,
|
||||
},
|
||||
{
|
||||
.option = {"exportable", 0, NULL, OPT_EXPORTABLE},
|
||||
.desc = "Convert to an secure AES key that is not export "
|
||||
"restricted. Exportable keys can be exported by means "
|
||||
"of wrapping them with a key encryption key (KEK).",
|
||||
.command = COMMAND_CONVERT,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
/***********************************************************/
|
||||
{
|
||||
.flags = UTIL_OPT_FLAG_SECTION,
|
||||
@@ -1891,7 +1938,8 @@ static int command_generate_clear(void)
|
||||
rc = generate_secure_key_clear(g.pkey_fd, g.pos_arg,
|
||||
g.keybits, g.xts,
|
||||
g.clearkeyfile, g.key_type,
|
||||
NULL, g.verbose);
|
||||
NULL, g.exportable, g.wrap_with_trusted,
|
||||
g.verbose);
|
||||
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1907,7 +1955,8 @@ static int command_generate_random(void)
|
||||
|
||||
rc = generate_secure_key_random(g.pkey_fd, g.pos_arg,
|
||||
g.keybits, g.xts, g.key_type,
|
||||
NULL, g.verbose);
|
||||
NULL, g.exportable, g.wrap_with_trusted,
|
||||
g.verbose);
|
||||
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1941,13 +1990,24 @@ static int command_generate_repository(void)
|
||||
}
|
||||
|
||||
if (g.clearkeyfile != NULL) {
|
||||
warnx("Option '----clearkey|-c' is not valid for "
|
||||
warnx("Option '--clearkey|-c' is not valid for "
|
||||
"generating a key in a KMS-bound repository, "
|
||||
"unless option '--local|-L' is also specified");
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (g.exportable) {
|
||||
warnx("Option '--exportable' is not supported for "
|
||||
"generating a key in a KMS-bound repository");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (g.wrap_with_trusted) {
|
||||
warnx("Option '--wrap-with-trusted' is not supported "
|
||||
"for generating a key in a KMS-bound repository");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
rc = perform_kms_login(&g.kms_info, g.verbose);
|
||||
if (rc != 0)
|
||||
rc = EXIT_FAILURE;
|
||||
@@ -1966,11 +2026,25 @@ static int command_generate_repository(void)
|
||||
if (g.key_type == NULL)
|
||||
g.key_type = KEY_TYPE_CCA_AESDATA;
|
||||
|
||||
if (g.wrap_with_trusted) {
|
||||
if (!g.exportable) {
|
||||
warnx("Option '--wrap-with-trusted' is only valid "
|
||||
"together with the '--exportable' option");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (strcasecmp(g.key_type, KEY_TYPE_EP11_AES) != 0) {
|
||||
warnx("Option '--wrap-with-trusted' is only valid for "
|
||||
"keys of type '%s'", KEY_TYPE_EP11_AES);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes,
|
||||
g.apqns, g.noapqncheck, g.sector_size,
|
||||
g.keybits, g.xts, g.clearkeyfile,
|
||||
g.volume_type, g.key_type, g.gen_passphrase,
|
||||
g.passphrase_file, g.pkey_fd);
|
||||
g.passphrase_file, g.exportable,
|
||||
g.wrap_with_trusted, g.pkey_fd);
|
||||
|
||||
out:
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
@@ -2007,6 +2081,18 @@ static int command_generate(void)
|
||||
"pvsecret import' instead", g.key_type);
|
||||
return -EXIT_FAILURE;
|
||||
}
|
||||
if (g.wrap_with_trusted) {
|
||||
if (!g.exportable) {
|
||||
warnx("Option '--wrap-with-trusted' is only valid "
|
||||
"together with the '--exportable' option");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (strcasecmp(g.key_type, KEY_TYPE_EP11_AES) != 0) {
|
||||
warnx("Option '--wrap-with-trusted' is only valid for "
|
||||
"keys of type '%s'", KEY_TYPE_EP11_AES);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (g.pos_arg != NULL) {
|
||||
if (g.volumes != NULL) {
|
||||
warnx("Option '--volumes|-l' is not valid for "
|
||||
@@ -2463,7 +2549,7 @@ static int command_import(void)
|
||||
rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes,
|
||||
g.apqns, g.noapqncheck, g.sector_size,
|
||||
g.pos_arg, g.volume_type, g.gen_passphrase,
|
||||
g.passphrase_file, &g.lib);
|
||||
g.passphrase_file, g.exportable, &g.lib);
|
||||
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -2816,14 +2902,17 @@ static int command_convert_file(void)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = restrict_key_export(&g.cca, output_key, output_key_size,
|
||||
g.verbose);
|
||||
if (rc != 0) {
|
||||
warnx("Export restricting the converted secure key has failed");
|
||||
if (!selected)
|
||||
print_msg_for_cca_envvars("secure AES key");
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
if (!g.exportable) {
|
||||
rc = restrict_key_export(&g.cca, output_key, output_key_size,
|
||||
g.verbose);
|
||||
if (rc != 0) {
|
||||
warnx("Export restricting the converted secure key "
|
||||
"has failed");
|
||||
if (!selected)
|
||||
print_msg_for_cca_envvars("secure AES key");
|
||||
rc = EXIT_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
pr_verbose("Secure key was converted successfully");
|
||||
@@ -2853,7 +2942,7 @@ static int command_convert_repository(void)
|
||||
}
|
||||
|
||||
rc = keystore_convert_key(g.keystore, g.name, g.key_type, g.noapqncheck,
|
||||
g.force, g.pkey_fd, &g.lib);
|
||||
g.force, g.exportable, g.pkey_fd, &g.lib);
|
||||
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -3519,6 +3608,12 @@ int main(int argc, char *argv[])
|
||||
case OPT_REMOVE_DUMMY_PASSPHRASE:
|
||||
g.remove_passphrase = 1;
|
||||
break;
|
||||
case OPT_EXPORTABLE:
|
||||
g.exportable = 1;
|
||||
break;
|
||||
case OPT_WRAP_WITH_TRUSTED:
|
||||
g.wrap_with_trusted = 1;
|
||||
break;
|
||||
case 'A':
|
||||
g.all = 1;
|
||||
break;
|
||||
|
||||
@@ -88,7 +88,7 @@ Without any options zmemtopo displays the tree view with nesting level 3.
|
||||
$ zmemtopo
|
||||
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
LPAR/LEVEL SIZE
|
||||
LPAR003 8G
|
||||
└LEVEL4_0 8G
|
||||
@@ -126,7 +126,7 @@ Display table format, by default nesting level 3 is displayed.
|
||||
$ zmemtopo -t
|
||||
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
LEVEL 4 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3
|
||||
LEVEL 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
|
||||
NR LPAR SUM
|
||||
@@ -150,7 +150,7 @@ Display reverse tree with nesting level 4.
|
||||
$ zmemtopo -r -l 4
|
||||
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
LEVEL/LPAR SIZE
|
||||
LEVEL4_0 388G
|
||||
│ ├LPAR003 8G
|
||||
@@ -178,7 +178,7 @@ Display reverse full tree. Entries that have no memory increments are visible.
|
||||
$ zmemtopo -rf
|
||||
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
.ft
|
||||
LEVEL/LPAR SIZE
|
||||
LEVEL4_0 388G
|
||||
@@ -240,7 +240,7 @@ Display tree with entries which have no memory increments.
|
||||
$ zmemtopo -f
|
||||
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
LPAR/LEVEL SIZE
|
||||
LPAR086 2G
|
||||
├LEVEL4_0 -
|
||||
@@ -287,7 +287,7 @@ Display json format.
|
||||
$ zmemtopo --format=json
|
||||
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
{
|
||||
"meta": {
|
||||
"api_level": "1",
|
||||
@@ -349,7 +349,7 @@ Display csv format.
|
||||
$ zmemtopo --format=csv
|
||||
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
"report_tod","report_partition_nr","increment_size","partition_nr","partition_name","parent_level","parent_entry_idx","level","entry_idx","increment_count"
|
||||
"0x0e082f818b066ce82000","74","1073741824","3","LPAR003","-","-","4","0","8"
|
||||
"0x0e082f818b066ce82000","74","1073741824","3","LPAR003","4","0","3","0","2"
|
||||
|
||||
@@ -10,8 +10,9 @@ include ../common.mak
|
||||
all: zpwr
|
||||
|
||||
OBJECTS = zpwr.o
|
||||
LDLIBS += -lm
|
||||
|
||||
zpwr: $(OBJECTS) $(rootdir)/libutil/libutil.a -lm
|
||||
zpwr: $(OBJECTS) $(rootdir)/libutil/libutil.a
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
|
||||
|
||||
@@ -154,7 +154,7 @@ CPU, storage or I/O resources to partitions.
|
||||
.SH "EXAMPLES"
|
||||
1. Display power readings in human readable format.
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
# zpwr
|
||||
LPAR CPU: 140.00 W
|
||||
LPAR Storage: 1.72 W
|
||||
@@ -171,7 +171,7 @@ Update interval: 10.00 s
|
||||
2. Perform two power measurements with a delay of 10 seconds and output the
|
||||
data in json format.
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
# zpwr --format json --delay 10 --count 2
|
||||
{
|
||||
"meta": {
|
||||
@@ -223,7 +223,7 @@ data in json format.
|
||||
.sp 1
|
||||
3. Display power readings in csv format and in stream mode.
|
||||
.nf
|
||||
.ft CW
|
||||
.ft CR
|
||||
# zpwr --format csv --stream
|
||||
"iteration","time","time_epoch_sec","time_epoch_nsec","update_interval","cpu","storage","io","total","unassigned_resources","infrastructure"
|
||||
"0","2025-01-08 07:17:05+0100","1736317025","592784684","10000000000","143000000","1708200","40000000","15550000000","7659162700","5337186700"
|
||||
|
||||
Reference in New Issue
Block a user