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 | |
|---|---|---|---|
|
|
c8d1aa55b4 | ||
|
|
25a70ac5a8 | ||
|
|
6235a51d7c | ||
|
|
e1aec24e84 | ||
|
|
d9a6fb3ccf | ||
|
|
a47dd4570a | ||
|
|
8fa1b5a00b | ||
|
|
e537ab902e | ||
|
|
f0fb4a180a | ||
|
|
f957d895cd | ||
|
|
73c3a9e684 | ||
|
|
2ee1387f34 | ||
|
|
10cefb3337 | ||
|
|
7b369318dd | ||
|
|
cf2fb296f0 | ||
|
|
6cbd81f997 | ||
|
|
2025cf751f | ||
|
|
5a052103ae | ||
|
|
2ced5853b4 | ||
|
|
13edbb7d5e | ||
|
|
1643af4578 | ||
|
|
c7ac1e63bd | ||
|
|
fa3dd0ec20 | ||
|
|
34146c3f92 | ||
|
|
298ede17f5 | ||
|
|
d66ace8121 | ||
|
|
2e89722ef0 | ||
|
|
c8ad5f57d0 | ||
|
|
d2317943c0 | ||
|
|
198c9fc052 | ||
|
|
b3551f50a8 | ||
|
|
4ff360e621 | ||
|
|
87e8fa18cf | ||
|
|
4bd135c3c7 | ||
|
|
f69350e8bd | ||
|
|
5768d55a08 | ||
|
|
538264a8cd | ||
|
|
7f09f0e4ad | ||
|
|
e36084d7f2 | ||
|
|
bd18166a21 | ||
|
|
a6f2c38891 | ||
|
|
029a3f8f52 | ||
|
|
dc87aef335 | ||
|
|
d712806b39 | ||
|
|
2734724432 | ||
|
|
6176509076 | ||
|
|
1780efae07 | ||
|
|
922e8cbb2f | ||
|
|
eef0dd4fa4 | ||
|
|
f93f437f8b | ||
|
|
4e6d43e2ac | ||
|
|
922d29ee49 | ||
|
|
25475bdf42 | ||
|
|
09c413b1be | ||
|
|
8944c2f60d | ||
|
|
ff3fe4bf43 | ||
|
|
5fe2e19277 | ||
|
|
b8cf861aea | ||
|
|
6ca3576dbf | ||
|
|
7ea425db5e | ||
|
|
330ddb7adb | ||
|
|
d69b9fab4a | ||
|
|
4e02dc8751 | ||
|
|
0c8be405ce | ||
|
|
24677dc561 | ||
|
|
7e7c38755d | ||
|
|
229cd6d194 | ||
|
|
776e5d41d7 | ||
|
|
8d7e8a87b5 | ||
|
|
e24d8c936c | ||
|
|
39b6c4a273 | ||
|
|
da9b96fb12 | ||
|
|
7d855acd07 | ||
|
|
f961a2bf90 | ||
|
|
9f93af614f | ||
|
|
d529ba07d3 | ||
|
|
0fffb05d01 | ||
|
|
92b8409207 | ||
|
|
bc3134ba5a | ||
|
|
c7f55a182f | ||
|
|
5078be45b8 | ||
|
|
2418ea93fc | ||
|
|
13cac5de44 | ||
|
|
b6687925cc | ||
|
|
7ee51182bf | ||
|
|
ec3e5b4a75 | ||
|
|
0b5aac3d79 |
12
.checkpatch.conf
Normal file
12
.checkpatch.conf
Normal file
@@ -0,0 +1,12 @@
|
||||
--max-line-length=100
|
||||
--strict
|
||||
|
||||
# Not Linux therefore don't expect a Linux tree
|
||||
--no-tree
|
||||
|
||||
# Ignore the following
|
||||
--ignore PREFER_KERNEL_TYPES
|
||||
--ignore EXECUTE_PERMISSIONS
|
||||
--ignore FILE_PATH_CHANGES
|
||||
--ignore NEW_TYPEDEFS
|
||||
--ignore SPDX_LICENSE_TAG
|
||||
134
.clang-format
Normal file
134
.clang-format
Normal file
@@ -0,0 +1,134 @@
|
||||
# Must be compatible with clang-format 14
|
||||
---
|
||||
AccessModifierOffset: "-4"
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveBitFields: AcrossComments
|
||||
AlignConsecutiveMacros: AcrossComments
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
BitFieldColonSpacing: Both
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: "100"
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: "8"
|
||||
ContinuationIndentWidth: "8"
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: false
|
||||
# git grep -h '^#define [^[:space:]]*\(for_each\|iterate\)[^[:space:]]*(' \
|
||||
# | sed "s,^#define \([^[:space:]]*\(for_each\|iterate\)[^[:space:]]*\)(.*$, - '\1'," \
|
||||
# | LC_ALL=C sort -u
|
||||
ForEachMacros:
|
||||
- "dfi_cpu_iterate"
|
||||
- "dfi_mem_chunk_iterate"
|
||||
- "dfo_chunk_iterate"
|
||||
- "for_each_rb_entry"
|
||||
- "ns_range_for_each"
|
||||
- "sd_cpu_item_enable_iterate"
|
||||
- "sd_cpu_item_iterate"
|
||||
- "sd_cpu_iterate"
|
||||
- "sd_cpu_type_iterate"
|
||||
- "sd_sys_item_enable_iterate"
|
||||
- "sd_sys_item_iterate"
|
||||
- "sd_sys_iterate"
|
||||
- "table_col_iterate"
|
||||
- "table_iterate_mark_keys"
|
||||
- "util_list_iterate"
|
||||
- "util_list_iterate_safe"
|
||||
- "util_opt_iterate"
|
||||
- "util_rec_iterate"
|
||||
IncludeBlocks: Preserve
|
||||
# Leave imports as is
|
||||
IncludeCategories:
|
||||
- Regex: .*
|
||||
Priority: 1
|
||||
IndentCaseLabels: false
|
||||
IndentGotoLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: "8"
|
||||
IndentWrappedFunctionNames: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ""
|
||||
MacroBlockEnd: ""
|
||||
MaxEmptyLinesToKeep: "1"
|
||||
NamespaceIndentation: Inner
|
||||
PackConstructorInitializers: CurrentLine
|
||||
PenaltyBreakAssignment: "10"
|
||||
PenaltyBreakBeforeFirstCallParameter: "30"
|
||||
PenaltyBreakComment: "10"
|
||||
PenaltyBreakFirstLessLess: "0"
|
||||
PenaltyBreakString: "10"
|
||||
PenaltyExcessCharacter: "100"
|
||||
PenaltyReturnTypeOnItsOwnLine: "60"
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatementsExceptForEachMacros
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: "1"
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
StatementMacros:
|
||||
- "STATIC_ASSERT"
|
||||
TabWidth: "8"
|
||||
TypenameMacros:
|
||||
- "STACK_OF"
|
||||
UseTab: Always
|
||||
WhitespaceSensitiveMacros:
|
||||
- "STRINGIFY"
|
||||
5
.codespellrc
Normal file
5
.codespellrc
Normal file
@@ -0,0 +1,5 @@
|
||||
[codespell]
|
||||
ignore-words-list = parm,parms
|
||||
skip = ''
|
||||
count = ''
|
||||
quiet-level = 3
|
||||
24
CHANGELOG.md
24
CHANGELOG.md
@@ -1,6 +1,29 @@
|
||||
Release history for s390-tools (MIT version)
|
||||
--------------------------------------------
|
||||
|
||||
* __v2.24.0 (2022-11-09)__
|
||||
|
||||
For Linux kernel version: 6.0
|
||||
|
||||
Add new tools / libraries:
|
||||
- Provide config files for checkpatch, codespell, and clang-format
|
||||
|
||||
Changes of existing tools:
|
||||
- dbginfo.sh: Collect log from various distro tools (YaST, DNF, Anaconda)
|
||||
- dbginfo.sh: add Kubernetes data collection
|
||||
- libutil: Introduce util_lockfile
|
||||
- zdev: Add site-aware device configuration
|
||||
- zdump: Add support to read Protected Virtualization dumps
|
||||
- zipl/boot: Add secure boot trailer
|
||||
|
||||
Bug Fixes:
|
||||
- ap_tools/ap-check: Reject start for control domains without usage
|
||||
- cpumf/lshwc: Fix incremented counter output
|
||||
- cpumf/pai: Fix core dump when summary flag set
|
||||
- dbginfo.sh: Ensure compatibility with /bin/dash shell
|
||||
- dbginfo.sh: Save dbginfo.sh version to dbginfo.log
|
||||
- zipl/src/zipl_helper.device-mapper: Fix bug in error path
|
||||
|
||||
* __v2.23.0 (2022-08-18)__
|
||||
|
||||
For Linux kernel version: 5.19
|
||||
@@ -24,7 +47,6 @@ Release history for s390-tools (MIT version)
|
||||
- pvattest: Fix dependency checking
|
||||
- zipl: Fix segmentation fault when no parmline is provided
|
||||
|
||||
|
||||
* __v2.22.0 (2022-06-20)__
|
||||
|
||||
For Linux kernel version: 5.18
|
||||
|
||||
@@ -4,8 +4,10 @@ Coding guidelines for s390-tools
|
||||
For s390-tools the preferred language is C. We provide libraries, e.g.
|
||||
[`libutil`](libutil) that should be used by all tools if possible.
|
||||
|
||||
The coding style is based on the Linux [kernel guidelines]. Therefore, use
|
||||
the [checkpatch] tool for verification before you submit a patch.
|
||||
The coding style is based on the Linux [kernel guidelines]. Therefore, use the
|
||||
[checkpatch] tool for verification before you submit a patch. s390-tools
|
||||
provides a CheckPatch configuration for this - see
|
||||
[`.checkpatch.conf`](.checkpatch.conf).
|
||||
|
||||
[kernel guidelines]: https://www.kernel.org/doc/html/latest/process/coding-style.html
|
||||
[checkpatch]: https://github.com/torvalds/linux/blob/master/scripts/checkpatch.pl
|
||||
@@ -17,6 +19,39 @@ This package started in 2001 and has a long "tradition" - therefore, older tools
|
||||
might not follow all recommendations. Note that when changing existing code,
|
||||
consistency could have priority over applying rules.
|
||||
|
||||
Automatic Code Formatting
|
||||
-------------------------
|
||||
|
||||
> **NOTE:** clang-format is a helpful tool but please don't use it blindly!
|
||||
|
||||
s390-tools provides a ClangFormat (https://clang.llvm.org/docs/ClangFormat.html)
|
||||
configuration file - see [`.clang-format`](.clang-format). It can be used to
|
||||
format your C/C++ code automatically.
|
||||
|
||||
Clang-format can format a single file or multiple files at once. For example, to
|
||||
format `main.c` in place, run the following command in a terminal:
|
||||
|
||||
```bash
|
||||
clang-format -i main.c
|
||||
```
|
||||
|
||||
In order to format only your current staged changes use the clang-format git
|
||||
plugin:
|
||||
|
||||
```bash
|
||||
git clang-format --staged
|
||||
```
|
||||
|
||||
See also `git clang-format -h`.
|
||||
|
||||
Automatic Editor Configuration
|
||||
------------------------------
|
||||
|
||||
s390-tools provides a EditorConfig (https://editorconfig.org/) configuration
|
||||
file - see [`.editorconfig`](.editorconfig). EditorConfig defines coding style
|
||||
rules (such as indentation size, use of tabs, etc.) and is supported by most
|
||||
common editors natively or via plugins.
|
||||
|
||||
Standard abbreviations
|
||||
----------------------
|
||||
|
||||
|
||||
22
README.md
22
README.md
@@ -60,6 +60,7 @@ Package contents
|
||||
|
||||
* zgetdump:
|
||||
Retrieve system dumps from either tapes, DASDs, SCSIs or NVMes.
|
||||
Decrypt Protected Virtualization (PV) dumps from IBM Secure Execution guests.
|
||||
|
||||
* qetharp:
|
||||
Read and flush the ARP cache on OSA Express network cards.
|
||||
@@ -309,16 +310,15 @@ build options:
|
||||
| net-snmp | `HAVE_SNMP` | osasnmpd |
|
||||
| glibc-static | `HAVE_LIBC_STATIC` | zfcpdump |
|
||||
| openssl | `HAVE_OPENSSL` | genprotimg, zkey, libekmfweb, |
|
||||
| | | libkmipclient, pvattest |
|
||||
| | | libkmipclient, pvattest, zgetdump |
|
||||
| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup |
|
||||
| json-c | `HAVE_JSONC` | zkey-cryptsetup, libekmfweb, |
|
||||
| | | libkmipclient |
|
||||
| glib2 | `HAVE_GLIB2` | genprotimg, pvattest |
|
||||
| glib2 | `HAVE_GLIB2` | genprotimg, pvattest, zgetdump |
|
||||
| libcurl | `HAVE_LIBCURL` | genprotimg, libekmfweb, libkmipclient,|
|
||||
| | | pvattest |
|
||||
| libxml2 | `HAVE_LIBXML2` | libkmipclient |
|
||||
| systemd | `HAVE_SYSTEMD` | hsavmcore |
|
||||
| liblockfile | `HAVE_LOCKFILE` | ap-check |
|
||||
| libudev | `HAVE_LIBUDEV` | cpacfstatsd |
|
||||
|
||||
This table lists additional build or install options:
|
||||
@@ -376,6 +376,13 @@ the different tools are provided:
|
||||
- Packages: blktrace, multipath-tools, sg3-utils
|
||||
- Tools: rsync, tar, lsscsi
|
||||
|
||||
* zgetdump
|
||||
For building zgetdump you need OpenSSL version 1.1.0 or newer
|
||||
installed (openssl-devel.rpm). Also required is glib2
|
||||
(glib2-devel.rpm) and zlib (zlib-devel.rpm).
|
||||
Tip: you may skip the zgetdump build by adding
|
||||
`HAVE_OPENSSL=0`, `HAVE_GLIB2=0`, or `HAVE_ZLIB=0`.
|
||||
|
||||
* cmsfs-fuse/zdsfs/hmcdrvfs/zgetdump:
|
||||
The tools cmsfs-fuse, zdsfs, hmcdrvfs, and zgetdump depend on FUSE.
|
||||
FUSE is provided by installing the fuse3 and libfuse3 packages and by a
|
||||
@@ -505,8 +512,7 @@ the different tools are provided:
|
||||
GNU awk for the build process.
|
||||
|
||||
* ap-check:
|
||||
For building the ap-check mdevctl callout utility you need liblockfile
|
||||
version 1.14 or newer installed (liblockfile-devel.rpm). Also required is
|
||||
json-c version 0.13 or newer (json-c-devel.rpm).
|
||||
Tip: you may skip ap-check build by adding `HAVE_LOCKFILE=0` or `HAVE_JSONC=0`
|
||||
to the make invocation.
|
||||
For building the ap-check mdevctl callout utility you need json-c version
|
||||
0.13 or newer (json-c-devel.rpm).
|
||||
Tip: you may skip ap-check build by adding `HAVE_JSONC=0` to the make
|
||||
invocation.
|
||||
|
||||
@@ -7,21 +7,14 @@ MDEVCTL_CALLOUTS = /etc/mdevctl.d/scripts.d/callouts/
|
||||
libs = $(rootdir)/libap/libap.a \
|
||||
$(rootdir)/libutil/libutil.a
|
||||
|
||||
ifeq (${HAVE_LOCKFILE},0)
|
||||
all:
|
||||
$(SKIP) HAVE_LOCKFILE=0
|
||||
|
||||
install:
|
||||
$(SKIP) HAVE_LOCKFILE=0
|
||||
|
||||
else ifeq (${HAVE_JSONC},0)
|
||||
ifeq (${HAVE_JSONC},0)
|
||||
all:
|
||||
$(SKIP) HAVE_JSONC=0
|
||||
|
||||
install:
|
||||
$(SKIP) HAVE_JSONC=0
|
||||
else
|
||||
LDLIBS += -llockfile -ljson-c
|
||||
LDLIBS += -ljson-c
|
||||
|
||||
all: ap-check
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ static void ap_check_cleanup(struct ap_check_anchor *anc)
|
||||
if (anc->dev)
|
||||
vfio_ap_device_free(anc->dev);
|
||||
if (anc->cleanup_lock)
|
||||
ap_release_lock();
|
||||
ap_release_lock_callout();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -685,6 +685,14 @@ static int ap_check_handle_start(struct ap_check_anchor *anc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Ensure device with control domains also has usage domains */
|
||||
if (util_list_is_empty(anc->dev->domains) &&
|
||||
!util_list_is_empty(anc->dev->controls)) {
|
||||
fprintf(stderr, "At least one usage domain must be specified\n");
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check against all other active vfio-ap devices */
|
||||
rc = check_other_mdevs_sysfs(anc);
|
||||
/* Check against the system sysfs values for apmask/aqmask */
|
||||
@@ -767,7 +775,7 @@ out:
|
||||
*/
|
||||
static int ap_check_handle_post(void)
|
||||
{
|
||||
return ap_release_lock();
|
||||
return ap_release_lock_callout();
|
||||
}
|
||||
|
||||
/* For the specified device, print the attributes to stdout in JSON format */
|
||||
|
||||
@@ -5,7 +5,7 @@ COMMON_INCLUDED = true
|
||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||
VERSION = 2
|
||||
RELEASE = 23
|
||||
RELEASE = 24
|
||||
PATCHLEVEL = 0
|
||||
DISTRELEASE = build-$(shell date +%Y%m%d)
|
||||
S390_TOOLS_RELEASE = $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||
@@ -120,7 +120,7 @@ endif
|
||||
define check_header_prereq
|
||||
$(shell printf "#include <%s>\n int main(void) {return 0;}\n" $1 | \
|
||||
( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) \
|
||||
$2 -o /dev/null -xc - ) >/dev/null 2>&1 && echo -n yes)
|
||||
$2 -o /dev/null -x c - ) >/dev/null 2>&1 && echo -n yes)
|
||||
endef
|
||||
|
||||
#
|
||||
@@ -133,7 +133,7 @@ endef
|
||||
# $5: Additional compiler & linker options (optional)
|
||||
#
|
||||
check_dep=\
|
||||
printf "\#include <%s>\n int main(void) {return 0;}\n" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) $5 -o /dev/null -xc - ) > /dev/null 2>&1; \
|
||||
printf "\#include <%s>\n int main(void) {return 0;}\n" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) $5 -o /dev/null -x c - ) > /dev/null 2>&1; \
|
||||
if [ $$? != 0 ]; \
|
||||
then \
|
||||
printf " REQCHK %s (%s)\n" $1 $2; \
|
||||
|
||||
@@ -356,6 +356,8 @@ static void line(char *header)
|
||||
putchar(',');
|
||||
printf("%ld", ctrname[i].total);
|
||||
comma = true;
|
||||
ctrname[i].total = 0;
|
||||
ctrname[i].hitcnt = false;
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
@@ -446,7 +448,7 @@ static bool add_countervalue(size_t idx, unsigned int cpu, unsigned long value)
|
||||
ctrname[idx].ccv = calloc(max_possible_cpus,
|
||||
sizeof(unsigned long));
|
||||
if (ctrname[idx].ccv)
|
||||
ctrname[idx].ccv[cpu] += value;
|
||||
ctrname[idx].ccv[cpu] = value;
|
||||
ctrname[idx].total += value;
|
||||
ctrname[idx].hitcnt = true;
|
||||
return true;
|
||||
@@ -458,10 +460,8 @@ static int test_read(struct s390_hwctr_read *read)
|
||||
size_t offset = 0;
|
||||
|
||||
/* Clear previous hit counters */
|
||||
for (unsigned int i = 0; i < max_possible_cpus; ++i) {
|
||||
check[i].sets_hit = 0;
|
||||
for (unsigned int i = 0; i < max_possible_cpus; ++i)
|
||||
check[i].cpu_hit = false;
|
||||
}
|
||||
|
||||
/* Iterate over all CPUs */
|
||||
for (unsigned int i = 0; i < read->no_cpus; ++i) {
|
||||
|
||||
@@ -426,7 +426,7 @@ static const char *evt_selector(struct perf_event_attr *pa)
|
||||
static void evt_show(__u64 evtnum, const char *evtsel, struct pai_event_out *ev)
|
||||
{
|
||||
if (summary) {
|
||||
if (ev->type == PERF_RECORD_SAMPLE)
|
||||
if (ev->type == PERF_RECORD_SAMPLE && ev->raw)
|
||||
evtraw_show(evtnum, ev->raw);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#define GETTEXT_PACKAGE "genprotimg"
|
||||
#include <glib.h>
|
||||
#include <glib/gi18n-lib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "boot/linux_layout.h"
|
||||
#include "boot/s390.h"
|
||||
|
||||
@@ -90,5 +90,6 @@ void ap_list_remove_all(struct util_list *list);
|
||||
int ap_get_lock(void);
|
||||
int ap_get_lock_callout(void);
|
||||
int ap_release_lock(void);
|
||||
int ap_release_lock_callout(void);
|
||||
|
||||
#endif /* LIB_AP_H */
|
||||
|
||||
26
include/lib/util_lockfile.h
Normal file
26
include/lib/util_lockfile.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @defgroup util_lockfile_h util_lockfile: File locking utility
|
||||
* @{
|
||||
* @brief Create file-based locks
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
#ifndef LIB_UTIL_LOCKFILE_H
|
||||
#define LIB_UTIL_LOCKFILE_H
|
||||
|
||||
#define UTIL_LOCKFILE_OK 0 /* Lock acquired/released successfully */
|
||||
#define UTIL_LOCKFILE_LOCK_FAIL 1 /* Lock already held, ran out of retries */
|
||||
#define UTIL_LOCKFILE_RELEASE_NONE 2 /* Lock not held */
|
||||
#define UTIL_LOCKFILE_RELEASE_FAIL 3 /* Lock not held by specified pid */
|
||||
#define UTIL_LOCKFILE_ERR 4 /* Other, unexpected error conditions */
|
||||
|
||||
int util_lockfile_lock(char *lockfile, int retries);
|
||||
int util_lockfile_parent_lock(char *lockfile, int retries);
|
||||
|
||||
int util_lockfile_release(char *lockfile);
|
||||
int util_lockfile_parent_release(char *lockfile);
|
||||
|
||||
#endif /** LIB_UTIL_LOCKFILE_H @} */
|
||||
@@ -32,6 +32,10 @@
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#ifndef sizeof_field
|
||||
#define sizeof_field(type, field) (sizeof((type *)0)->field)
|
||||
#endif
|
||||
|
||||
#ifdef UNUSED
|
||||
#elif defined(__GNUC__)
|
||||
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
|
||||
@@ -50,6 +54,10 @@
|
||||
#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask))
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#ifndef ROUNDUP
|
||||
# define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
|
||||
#endif
|
||||
|
||||
#define RELEASE_STRING STRINGIFY (S390_TOOLS_RELEASE)
|
||||
#define TOOLS_LIBDIR STRINGIFY (S390_TOOLS_LIBDIR)
|
||||
#define TOOLS_SYSCONFDIR STRINGIFY (S390_TOOLS_SYSCONFDIR)
|
||||
@@ -62,6 +70,7 @@
|
||||
#define __may_alias __attribute__((may_alias))
|
||||
#define __section(x) __attribute__((__section__(#x)))
|
||||
#define __noinline __attribute__((__noinline__))
|
||||
#define __big_endian
|
||||
/* The Linux kernel (in stddef.h) and glibc (sys/cdefs.h) define
|
||||
* __always_inline. Therefore undefine it first to allow the headers
|
||||
* to be included first.
|
||||
|
||||
@@ -68,6 +68,7 @@ typedef enum {
|
||||
PV_CERT_ERROR_MALFORMED_ROOT_CA,
|
||||
PV_CERT_ERROR_NO_CRL,
|
||||
PV_CERT_ERROR_NO_CRLDP,
|
||||
PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
PV_CERT_ERROR_NO_ISSUER_IBM_Z_FOUND,
|
||||
PV_CERT_ERROR_NO_PUBLIC_KEY,
|
||||
PV_CERT_ERROR_READ_CERTIFICATE,
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
#include "libpv/glib-helper.h"
|
||||
|
||||
#include <glib/gi18n-lib.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "libpv/openssl-compat.h"
|
||||
#include "libpv/macros.h"
|
||||
|
||||
@@ -42,11 +42,14 @@ enum PvCryptoMode {
|
||||
PV_DECRYPT,
|
||||
};
|
||||
|
||||
/** pv_get_openssl_error:
|
||||
/** pv_get_openssl_errors:
|
||||
*
|
||||
* Returns: (transfer full): String representing the error.
|
||||
* Returns the last OpenSSL error messages.
|
||||
* Caller is responsible to free the returned value.
|
||||
*
|
||||
* Returns: String representing the error.
|
||||
*/
|
||||
const char *pv_get_openssl_error(void);
|
||||
char *pv_get_openssl_errors(void);
|
||||
|
||||
/**
|
||||
* pv_BIO_reset:
|
||||
@@ -181,7 +184,6 @@ typedef enum {
|
||||
PV_CRYPTO_ERROR_KEYGENERATION,
|
||||
PV_CRYPTO_ERROR_RANDOMIZATION,
|
||||
PV_CRYPTO_ERROR_READ_FILE,
|
||||
PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
PV_CRYPTO_ERROR_NO_MATCH_TAG,
|
||||
} PvCryptoErrors;
|
||||
|
||||
|
||||
@@ -102,9 +102,12 @@ FILE *pv_file_open(const char *filename, const char *mode, GError **error);
|
||||
|
||||
/** pv_gbytes_memcpy:
|
||||
*
|
||||
* memcpy with size check.
|
||||
* memcpy with size check. In case @dst_size is smaller than the size of @src
|
||||
* the error value %NULL is returned and @copied and @dst are left unchanged.
|
||||
*
|
||||
* If @dst and @src data overlap, the behavior is undefined.
|
||||
*/
|
||||
void *pv_gbytes_memcpy(void *dst, size_t dst_size, GBytes *src);
|
||||
void *pv_gbytes_memcpy(void *dst, size_t dst_size, GBytes *src, size_t *copied);
|
||||
|
||||
#define PV_GLIB_HELPER_ERROR g_quark_from_static_string("pv-glib-helper_error-quark")
|
||||
typedef enum {
|
||||
|
||||
@@ -10,7 +10,12 @@
|
||||
#ifndef LIBPV_MACROS_H
|
||||
#define LIBPV_MACROS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PV_NONNULL(...)
|
||||
#define DO_PRAGMA(x) _Pragma(#x)
|
||||
|
||||
/* Most significant bit */
|
||||
#define PV_MSB(idx) ((uint64_t)1 << (63 - (idx)))
|
||||
|
||||
#endif /* LIBPV_MACROS_H */
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "libpv/crypto.h"
|
||||
#include "libpv/macros.h"
|
||||
|
||||
/* Magic number which is used to identify the file containing the PV
|
||||
* header
|
||||
@@ -22,20 +23,17 @@
|
||||
#define PV_MAGIC_NUMBER 0x49424d5365634578ULL
|
||||
#define PV_VERSION_1 0x00000100U
|
||||
|
||||
/* Internal helper macro */
|
||||
#define __PV_BIT(nr) (1ULL << (63 - (nr)))
|
||||
|
||||
/* Plaintext control flags */
|
||||
/* dumping of the configuration is allowed */
|
||||
#define PV_PCF_ALLOW_DUMPING __PV_BIT(34)
|
||||
#define PV_PCF_ALLOW_DUMPING PV_MSB(34)
|
||||
/* prevent Ultravisor decryption during unpack operation */
|
||||
#define PV_PCF_NO_DECRYPTION __PV_BIT(35)
|
||||
#define PV_PCF_NO_DECRYPTION PV_MSB(35)
|
||||
/* PCKMO encrypt-DEA/TDEA-key functions allowed */
|
||||
#define PV_PCF_PCKMO_DEA_TDEA __PV_BIT(56)
|
||||
#define PV_PCF_PCKMO_DEA_TDEA PV_MSB(56)
|
||||
/* PCKMO encrypt-AES-key functions allowed */
|
||||
#define PV_PCF_PCKMO_AES __PV_BIT(57)
|
||||
#define PV_PCF_PCKMO_AES PV_MSB(57)
|
||||
/* PCKMO encrypt-ECC-key functions allowed */
|
||||
#define PV_PCF_PCKM_ECC __PV_BIT(58)
|
||||
#define PV_PCF_PCKM_ECC PV_MSB(58)
|
||||
|
||||
/* maxima for the PV version 1 */
|
||||
#define PV_V1_IPIB_MAX_SIZE PAGE_SIZE
|
||||
|
||||
@@ -2,17 +2,6 @@ include ../common.mak
|
||||
|
||||
lib = libap.a
|
||||
|
||||
check-dep-lock:
|
||||
touch check-dep-lock
|
||||
ifneq (${HAVE_LOCKFILE},0)
|
||||
$(call check_dep, \
|
||||
"libap", \
|
||||
"lockfile.h", \
|
||||
"liblockfile-devel", \
|
||||
"HAVE_LOCKFILE=0")
|
||||
ALL_CPPFLAGS += -DHAVE_LOCKFILE
|
||||
endif
|
||||
|
||||
check-dep-json:
|
||||
touch check-dep-json
|
||||
ifneq (${HAVE_JSONC},0)
|
||||
@@ -29,7 +18,7 @@ objects = ap.o
|
||||
|
||||
$(lib): $(objects)
|
||||
|
||||
$(objects): check-dep-lock check-dep-json
|
||||
$(objects): check-dep-json
|
||||
|
||||
install: all
|
||||
|
||||
|
||||
45
libap/ap.c
45
libap/ap.c
@@ -22,13 +22,10 @@
|
||||
#include <json-c/json.h>
|
||||
#endif /* HAVE_JSONC */
|
||||
|
||||
#ifdef HAVE_LOCKFILE
|
||||
#include <lockfile.h>
|
||||
#endif /* HAVE_LOCKFILE */
|
||||
|
||||
#include "lib/ap.h"
|
||||
#include "lib/util_file.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_lockfile.h"
|
||||
#include "lib/util_panic.h"
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/util_udev.h"
|
||||
@@ -701,33 +698,28 @@ void ap_list_remove_all(struct util_list *list)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LOCKFILE
|
||||
static int ap_lockfile_create(int flags)
|
||||
{
|
||||
return lockfile_create(AP_LOCKFILE, AP_LOCK_RETRIES, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire the ap config lock using this process PID (L_PID)
|
||||
* Acquire the ap config lock using this Process ID
|
||||
*
|
||||
* @retval 0 Lock acquired on behalf of this process
|
||||
*
|
||||
* @retval 0 Lock successfully acquired on behalf of L_PID
|
||||
* @retval != 0 Error, lock was not obtained
|
||||
*/
|
||||
int ap_get_lock(void)
|
||||
{
|
||||
return ap_lockfile_create(L_PID);
|
||||
return util_lockfile_lock(AP_LOCKFILE, AP_LOCK_RETRIES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire the ap config lock using the parent process PID (L_PPID) -- intended
|
||||
* for use by the mdevctl callout ap-check utility
|
||||
* Acquire the ap config lock using the Parent Process ID -- intended for use
|
||||
* by the mdevctl callout ap-check utility
|
||||
*
|
||||
* @retval 0 Lock successfully acquired on behalf of L_PPID
|
||||
* @retval 0 Lock acquired on behalf of parent process
|
||||
* @retval != 0 Error, lock was not obtained
|
||||
*/
|
||||
int ap_get_lock_callout(void)
|
||||
{
|
||||
return ap_lockfile_create(L_PPID);
|
||||
return util_lockfile_parent_lock(AP_LOCKFILE, AP_LOCK_RETRIES);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -738,23 +730,10 @@ int ap_get_lock_callout(void)
|
||||
*/
|
||||
int ap_release_lock(void)
|
||||
{
|
||||
return lockfile_remove(AP_LOCKFILE);
|
||||
}
|
||||
#else
|
||||
/* If no liblockfile, actions are performed without acquiring the file lock */
|
||||
int ap_get_lock(void)
|
||||
{
|
||||
return 0;
|
||||
return util_lockfile_release(AP_LOCKFILE);
|
||||
}
|
||||
|
||||
int ap_get_lock_callout(void)
|
||||
int ap_release_lock_callout(void)
|
||||
{
|
||||
return 0;
|
||||
return util_lockfile_parent_release(AP_LOCKFILE);
|
||||
}
|
||||
|
||||
int ap_release_lock(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HAVE_LOCKFILE */
|
||||
|
||||
16
libpv/cert.c
16
libpv/cert.c
@@ -290,7 +290,9 @@ static char *pv_X509_NAME_oneline(const X509_NAME *name)
|
||||
long len;
|
||||
|
||||
if (X509_NAME_print_ex(key_bio, name, 0, XN_FLAG_RFC2253) == -1) {
|
||||
g_warning(_("Cannot receive X509-NAME from CRL: %s"), pv_get_openssl_error());
|
||||
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
|
||||
|
||||
g_warning(_("Cannot receive X509-NAME from CRL: %s"), openssl_err_msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -790,7 +792,7 @@ int pv_store_set_verify_param(X509_STORE *store, GError **error)
|
||||
return 0;
|
||||
|
||||
error:
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
|
||||
_("X509 store initialization failed"));
|
||||
return -1;
|
||||
}
|
||||
@@ -1375,14 +1377,14 @@ int pv_check_chain_parameters(const STACK_OF_X509 *chain, GError **error)
|
||||
|
||||
ca_x509_subject = X509_get_subject_name(ca);
|
||||
if (!ca_x509_subject) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
|
||||
_("subject of the root CA cannot be retrieved"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ca_subject = pv_X509_NAME_oneline(ca_x509_subject);
|
||||
if (!ca_subject) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
|
||||
_("subject name of the root CA cannot be retrieved"));
|
||||
return -1;
|
||||
}
|
||||
@@ -1476,11 +1478,11 @@ static STACK_OF_X509 *get_ibm_signing_certs(STACK_OF_X509 *certs, GError **error
|
||||
ibm_signing_certs = pv_remove_ibm_signing_certs(certs);
|
||||
ibm_signing_certs_count = sk_X509_num(ibm_signing_certs);
|
||||
if (ibm_signing_certs_count < 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
_("Specify at least one IBM Z signing key"));
|
||||
return NULL;
|
||||
} else if (ibm_signing_certs_count > 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_NO_IBM_Z_SIGNING_KEY,
|
||||
_("Specify only one IBM Z signing key"));
|
||||
return NULL;
|
||||
}
|
||||
@@ -1511,7 +1513,7 @@ static gboolean download_crls(X509_STORE *trusted, PvCertWithPathList *host_key_
|
||||
X509_CRL *crl = sk_X509_CRL_value(downloaded_ibm_signing_crls, i);
|
||||
|
||||
if (X509_STORE_add_crl(trusted, crl) != 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||||
g_set_error(error, PV_CERT_ERROR, PV_CERT_ERROR_INTERNAL,
|
||||
_("failed to load CRL"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -19,18 +19,18 @@
|
||||
#include "libpv/glib-helper.h"
|
||||
#include "libpv/hash.h"
|
||||
|
||||
const char *pv_get_openssl_error(void)
|
||||
char *pv_get_openssl_errors(void)
|
||||
{
|
||||
const char *ret;
|
||||
BIO *bio;
|
||||
char *ret;
|
||||
char *buf;
|
||||
BIO *bio;
|
||||
long len;
|
||||
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
ERR_print_errors(bio);
|
||||
len = BIO_get_mem_data(bio, &buf);
|
||||
if (len < 0)
|
||||
ret = "Cannot receive OpenSSL error message.";
|
||||
if (len <= 0 || !buf)
|
||||
ret = g_strdup("Cannot receive OpenSSL error message.");
|
||||
else
|
||||
ret = g_strndup(buf, (size_t)len);
|
||||
BIO_free(bio);
|
||||
@@ -276,7 +276,7 @@ int64_t pv_gcm_decrypt(GBytes *cipher, GBytes *aad, GBytes *tag, const PvCipherP
|
||||
}
|
||||
|
||||
GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes *salt, GBytes *info,
|
||||
const EVP_MD *md, G_GNUC_UNUSED GError **error)
|
||||
const EVP_MD *md, GError **error)
|
||||
{
|
||||
const unsigned char *salt_data, *key_data, *info_data;
|
||||
g_autoptr(EVP_PKEY_CTX) ctx = NULL;
|
||||
@@ -354,7 +354,6 @@ GBytes *pv_hkdf_extract_and_expand(size_t derived_key_len, GBytes *key, GBytes *
|
||||
if (EVP_PKEY_derive(ctx, derived_key, &derived_key_len) != 1) {
|
||||
g_set_error(error, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_HKDF_FAIL,
|
||||
"FAILED to derive key via HKDF");
|
||||
printf("%s\n", pv_get_openssl_error());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,12 +168,14 @@ GBytes *pv_file_get_content_as_secure_bytes(const char *filename)
|
||||
return pv_sec_gbytes_new_take(g_steal_pointer(&data), data_size);
|
||||
}
|
||||
|
||||
void *pv_gbytes_memcpy(void *dst, size_t dst_size, GBytes *src)
|
||||
void *pv_gbytes_memcpy(void *dst, size_t dst_size, GBytes *src, size_t *copied)
|
||||
{
|
||||
size_t src_size;
|
||||
const void *src_data = g_bytes_get_data(src, &src_size);
|
||||
|
||||
if (dst_size < src_size)
|
||||
return NULL;
|
||||
if (copied)
|
||||
*copied = src_size;
|
||||
return memcpy(dst, src_data, src_size);
|
||||
}
|
||||
|
||||
12
libpv/hash.c
12
libpv/hash.c
@@ -97,8 +97,10 @@ HMAC_CTX *pv_hmac_ctx_new(GBytes *key, const EVP_MD *md, GError **error)
|
||||
key_data = g_bytes_get_data(key, &key_size);
|
||||
|
||||
if (HMAC_Init_ex(ctx, key_data, (int)key_size, md, NULL) != 1) {
|
||||
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
|
||||
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
"unable to create HMAC context: %s", pv_get_openssl_error());
|
||||
"unable to create HMAC context: %s", openssl_err_msg);
|
||||
return NULL;
|
||||
}
|
||||
return g_steal_pointer(&ctx);
|
||||
@@ -110,8 +112,10 @@ int pv_hmac_ctx_update_raw(HMAC_CTX *ctx, const void *buf, size_t size, GError *
|
||||
return 0;
|
||||
|
||||
if (HMAC_Update(ctx, buf, size) != 1) {
|
||||
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
|
||||
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
"unable to add data to HMAC context: %s", pv_get_openssl_error());
|
||||
"unable to add data to HMAC context: %s", openssl_err_msg);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -139,8 +143,10 @@ GBytes *pv_hamc_ctx_finalize(HMAC_CTX *ctx, GError **error)
|
||||
hmac = g_malloc0((unsigned int)md_size);
|
||||
|
||||
if (HMAC_Final(ctx, hmac, &hmac_size) != 1) {
|
||||
g_autofree char *openssl_err_msg = pv_get_openssl_errors();
|
||||
|
||||
g_set_error(error, PV_HASH_ERROR, PV_HASH_ERROR_INTERNAL,
|
||||
"unable to calculate HMAC: %s", pv_get_openssl_error());
|
||||
"unable to calculate HMAC: %s", openssl_err_msg);
|
||||
return NULL;
|
||||
}
|
||||
return g_bytes_new_take(g_steal_pointer(&hmac), hmac_size);
|
||||
|
||||
@@ -107,13 +107,12 @@ const char *util_arch_machine_type_to_str(int type)
|
||||
*/
|
||||
unsigned long util_arch_hsa_maxsize(void)
|
||||
{
|
||||
unsigned long size = HSA_SIZE_32M;
|
||||
int type;
|
||||
|
||||
type = util_arch_machine_type();
|
||||
if (type != UTIL_ARCH_MACHINE_TYPE_UNKNOWN &&
|
||||
type >= UTIL_ARCH_MACHINE_TYPE_Z15)
|
||||
size = HSA_SIZE_512M;
|
||||
|
||||
return size;
|
||||
switch (util_arch_machine_type()) {
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z15_T02:
|
||||
case UTIL_ARCH_MACHINE_TYPE_Z16:
|
||||
return HSA_SIZE_512M;
|
||||
default:
|
||||
return HSA_SIZE_32M;
|
||||
}
|
||||
}
|
||||
|
||||
300
libutil/util_lockfile.c
Normal file
300
libutil/util_lockfile.c
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* util - Utility function library
|
||||
*
|
||||
* Created file-based locks
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_lockfile.h"
|
||||
#include "lib/util_panic.h"
|
||||
|
||||
#define WAITPID 120 /* Time to wait for pid to be written */
|
||||
#define WAITINC 5 /* Additional time to wait each retry */
|
||||
#define MAXWAIT 60 /* Maximum wait between retries */
|
||||
#define BUFSIZE 40 /* Buffer must be large enough to fit pid string */
|
||||
|
||||
/**
|
||||
* Check if there is a process that exists with the specified identifier.
|
||||
*
|
||||
* @param[in] pid Process Identifier to check
|
||||
*
|
||||
* @retval true Process exists or we lack privelege to check
|
||||
* @retval false Process does not exist
|
||||
*/
|
||||
static bool pid_exists(int pid)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Use sig 0 to determine if the owner is still alive */
|
||||
rc = kill(pid, 0);
|
||||
if (rc != 0) {
|
||||
switch (errno) {
|
||||
case EPERM:
|
||||
/* Privilege issue, just assume PID exists */
|
||||
break;
|
||||
case ESRCH:
|
||||
/* PID does not exist, this lock is stale */
|
||||
return false;
|
||||
default:
|
||||
util_assert(false, "Unexpected return from kill: %d\n", rc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for an existing lock that is deemed stale (either the associated PID
|
||||
* is gone or no PID has been written to the file in a reasonable timeframe).
|
||||
* In the case a stale lock is found, remove it.
|
||||
*
|
||||
* @param[in] lockfile Path to the lock file
|
||||
*
|
||||
* @retval 0 Either no lock or stale lock was found and removed
|
||||
* @retval 1 Lock is held by another PID and is not stale
|
||||
*/
|
||||
static int handle_stale_lock(char *lockfile)
|
||||
{
|
||||
int fd, rc, pid, len;
|
||||
struct stat info;
|
||||
char buf[BUFSIZE];
|
||||
time_t curr;
|
||||
|
||||
fd = open(lockfile, O_RDONLY);
|
||||
|
||||
if (fd >= 0) {
|
||||
/* Lock exists, see who owns it */
|
||||
len = read(fd, buf, sizeof(buf));
|
||||
if (len > 0) {
|
||||
buf[len] = 0; /* Ensure null terminated string */
|
||||
pid = atoi(buf);
|
||||
if (!pid_exists(pid)) {
|
||||
/* Stale lock detected unlink and retry now */
|
||||
close(fd);
|
||||
unlink(lockfile);
|
||||
return 0;
|
||||
} else if (pid != 0) {
|
||||
/* Lock is held by an active pid, delay */
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* If we reach this point, the PID was 0 which is
|
||||
* unexpected. Proceed under the assumption that the
|
||||
* proper PID hasn't been written yet.
|
||||
*/
|
||||
}
|
||||
/*
|
||||
* PID hasn't been written yet? Either a bad lock or a very
|
||||
* new one.
|
||||
*/
|
||||
time(&curr);
|
||||
rc = fstat(fd, &info);
|
||||
close(fd);
|
||||
if (rc != 0) {
|
||||
/* Can't read file anymore, retry now */
|
||||
return 0;
|
||||
}
|
||||
if (curr > info.st_mtime + WAITPID) {
|
||||
/*
|
||||
* PID should be in the file within 2 minutes,
|
||||
* something went wrong. Treat as stale.
|
||||
*/
|
||||
unlink(lockfile);
|
||||
return 0;
|
||||
}
|
||||
/* Otherwise, assume file was newly created and delay */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Couldn't open, try again immediately */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to create a lockfile at the specified path.
|
||||
*
|
||||
* @param[in] lockfile Path to the lock file
|
||||
* @param[in] retries Number of times to retry if lock fails initially
|
||||
* @param[in] pid PID to use for lock ownership
|
||||
*
|
||||
* @retval 0 Lock created with PID as owner
|
||||
* @retval !=0 Lock was not created
|
||||
*/
|
||||
static int do_lockfile_lock(char *lockfile, unsigned int retries, int pid)
|
||||
{
|
||||
int fd, plen, len, rc = 0, snooze = 0;
|
||||
unsigned int tries = retries + 1;
|
||||
char buf[BUFSIZE];
|
||||
char *tpath;
|
||||
|
||||
if (!lockfile)
|
||||
return UTIL_LOCKFILE_ERR;
|
||||
|
||||
plen = snprintf(buf, sizeof(buf), "%d\n", pid);
|
||||
if (plen < 0 || (plen > ((int)sizeof(buf) - 1)))
|
||||
return UTIL_LOCKFILE_ERR;
|
||||
|
||||
/* Allocate temporary lock file with a sufficiently unique path */
|
||||
len = util_asprintf(&tpath, "%s%05d%02x", lockfile, getpid(),
|
||||
(unsigned int)time(NULL) & 255);
|
||||
if (len < 0)
|
||||
return UTIL_LOCKFILE_ERR;
|
||||
|
||||
/* Open the temporary lockfile, write the specified pid */
|
||||
fd = open(tpath, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0644);
|
||||
if (fd < 0) {
|
||||
rc = UTIL_LOCKFILE_ERR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = write(fd, buf, plen);
|
||||
if (close(fd) != 0) {
|
||||
rc = UTIL_LOCKFILE_ERR;
|
||||
goto cleanup;
|
||||
}
|
||||
if (len != plen) {
|
||||
/* Failed to write the temp lockfile, bail out */
|
||||
rc = UTIL_LOCKFILE_ERR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Link the temprorary file to the real path */
|
||||
do {
|
||||
rc = link(tpath, lockfile);
|
||||
if (rc == 0) {
|
||||
/* Lock successfully acquired */
|
||||
rc = UTIL_LOCKFILE_OK;
|
||||
goto cleanup;
|
||||
}
|
||||
/* Lock already held - check for stale lock */
|
||||
rc = handle_stale_lock(lockfile);
|
||||
/* Only wait if the lock was not stale */
|
||||
if (rc != 0) {
|
||||
tries--;
|
||||
if (tries > 0) {
|
||||
snooze += WAITINC;
|
||||
snooze = (snooze > MAXWAIT) ? MAXWAIT : snooze;
|
||||
sleep(snooze);
|
||||
}
|
||||
}
|
||||
} while (tries > 0);
|
||||
|
||||
/* Exhausted specified number of retries, exit on failure */
|
||||
rc = UTIL_LOCKFILE_LOCK_FAIL;
|
||||
|
||||
cleanup:
|
||||
unlink(tpath);
|
||||
free(tpath);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to release a lockfile at the specified path.
|
||||
*
|
||||
* @param[in] lockfile Path to the lock file
|
||||
* @param[in] pid PID that should own the lock
|
||||
*
|
||||
* @retval 0 Lock released
|
||||
* @retval !=0 Lock was not released or did not exist
|
||||
*/
|
||||
static int do_lockfile_release(char *lockfile, int pid)
|
||||
{
|
||||
int fd, len, lpid;
|
||||
char buf[BUFSIZE];
|
||||
|
||||
if (!lockfile)
|
||||
return UTIL_LOCKFILE_ERR;
|
||||
|
||||
/* Open lockfile, read the owning pid if it exists */
|
||||
fd = open(lockfile, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return UTIL_LOCKFILE_RELEASE_NONE;
|
||||
len = read(fd, buf, sizeof(buf));
|
||||
close(fd);
|
||||
if (len <= 0)
|
||||
return UTIL_LOCKFILE_RELEASE_FAIL;
|
||||
buf[len] = 0;
|
||||
lpid = atoi(buf);
|
||||
|
||||
/* Only release the lock if its held by the right pid */
|
||||
if (pid != lpid)
|
||||
return UTIL_LOCKFILE_RELEASE_FAIL;
|
||||
|
||||
unlink(lockfile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to create a lockfile owned by this process at the specified path.
|
||||
*
|
||||
* @param[in] lockfile Path to the lock file
|
||||
* @param[in] retries Number of times to retry if lock fails initially
|
||||
*
|
||||
* @retval 0 Lock created
|
||||
* @retval !=0 Lock was not created
|
||||
*/
|
||||
int util_lockfile_lock(char *lockfile, int retries)
|
||||
{
|
||||
return do_lockfile_lock(lockfile, retries, getpid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to create a lockfile owned by the parent of this process at the
|
||||
* specified path.
|
||||
*
|
||||
* @param[in] lockfile Path to the lock file
|
||||
* @param[in] retries Number of times to retry if lock fails initially
|
||||
*
|
||||
* @retval 0 Lock created
|
||||
* @retval !=0 Lock was not created
|
||||
*/
|
||||
int util_lockfile_parent_lock(char *lockfile, int retries)
|
||||
{
|
||||
return do_lockfile_lock(lockfile, retries, getppid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to release a lockfile owned by this process at the specified path.
|
||||
*
|
||||
* @param[in] lockfile Path to the lock file
|
||||
*
|
||||
* @retval 0 Lock released
|
||||
* @retval !=0 Lock was not released or did not exist
|
||||
*/
|
||||
int util_lockfile_release(char *lockfile)
|
||||
{
|
||||
return do_lockfile_release(lockfile, getpid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to release a lockfile owned by the parent of this process at the
|
||||
* specified path.
|
||||
*
|
||||
* @param[in] lockfile Path to the lock file
|
||||
*
|
||||
* @retval 0 Lock released
|
||||
* @retval !=0 Lock was not released or did not exist
|
||||
*/
|
||||
int util_lockfile_parent_release(char *lockfile)
|
||||
{
|
||||
return do_lockfile_release(lockfile, getppid());
|
||||
}
|
||||
232
libutil/util_lockfile_example.c
Normal file
232
libutil/util_lockfile_example.c
Normal file
@@ -0,0 +1,232 @@
|
||||
/**
|
||||
* util_lockfile_example - Example program for util_lockfile
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
//! [code]
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "lib/util_opt.h"
|
||||
#include "lib/util_prg.h"
|
||||
#include "lib/util_lockfile.h"
|
||||
|
||||
static const struct util_prg prg = {
|
||||
.desc = "Example for util_lockfile.",
|
||||
.copyright_vec = { {
|
||||
.owner = "IBM Corp.",
|
||||
.pub_first = 2022,
|
||||
.pub_last = 2022,
|
||||
},
|
||||
UTIL_PRG_COPYRIGHT_END }
|
||||
};
|
||||
|
||||
static struct util_opt opt_vec[] = {
|
||||
UTIL_OPT_SECTION("OPTIONS"),
|
||||
{
|
||||
.option = { "file", required_argument, NULL, 'f' },
|
||||
.argument = "PATH",
|
||||
.desc = "Use the specified path for a lockfile.",
|
||||
},
|
||||
{
|
||||
.option = { "lock", required_argument, NULL, 'l' },
|
||||
.argument = "RETRIES",
|
||||
.desc = "Acquire the specified file lock using the parent "
|
||||
"process id of this process. If not immediately "
|
||||
"successful, retry for the specified number of times",
|
||||
},
|
||||
{
|
||||
.option = { "release", 0, NULL, 'r' },
|
||||
.desc = "Release the specified lock file using the parent "
|
||||
"process id",
|
||||
},
|
||||
{
|
||||
.option = { "lock-and-release", required_argument, NULL, 'L' },
|
||||
.argument = "RETRIES",
|
||||
.desc = "Acquire the specified file lock using this process "
|
||||
"id, then release it. If not immediately successful, "
|
||||
"retry for the specified number of times.",
|
||||
},
|
||||
UTIL_OPT_HELP,
|
||||
UTIL_OPT_VERSION,
|
||||
UTIL_OPT_END
|
||||
};
|
||||
|
||||
enum prg_action {
|
||||
ACTION_NONE = 0,
|
||||
ACTION_LOCK,
|
||||
ACTION_RELEASE,
|
||||
ACTION_LOCK_AND_RELEASE,
|
||||
};
|
||||
|
||||
static void print_single_action_error(void)
|
||||
{
|
||||
warnx("Only a single action (--lock, --lock-parent, --release, "
|
||||
"--release-parent) is allowed");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
enum prg_action action_id = ACTION_NONE;
|
||||
int opt, rc, retries = 0;
|
||||
char *endp, *path = NULL;
|
||||
|
||||
util_prg_init(&prg);
|
||||
util_opt_init(opt_vec, NULL);
|
||||
|
||||
while (1) {
|
||||
opt = util_opt_getopt_long(argc, argv);
|
||||
if (opt == -1)
|
||||
break;
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
path = optarg;
|
||||
break;
|
||||
case 'L':
|
||||
if (action_id != ACTION_NONE) {
|
||||
print_single_action_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
retries = strtol(optarg, &endp, 0);
|
||||
if (*optarg == '\0' || *endp != '\0' || retries < 0) {
|
||||
warnx("Invalid retry value for "
|
||||
"--lock-and-release: '%s'",
|
||||
optarg);
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
action_id = ACTION_LOCK_AND_RELEASE;
|
||||
break;
|
||||
case 'l':
|
||||
if (action_id != ACTION_NONE) {
|
||||
print_single_action_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
retries = strtol(optarg, &endp, 0);
|
||||
if (*optarg == '\0' || *endp != '\0' || retries < 0) {
|
||||
warnx("Invalid retry value for "
|
||||
"--parent-lock: '%s'",
|
||||
optarg);
|
||||
util_prg_print_parse_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
action_id = ACTION_LOCK;
|
||||
break;
|
||||
case 'r':
|
||||
if (action_id != ACTION_NONE) {
|
||||
print_single_action_error();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
action_id = ACTION_RELEASE;
|
||||
break;
|
||||
case 'h':
|
||||
util_prg_print_help();
|
||||
util_opt_print_help();
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'v':
|
||||
util_prg_print_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
default:
|
||||
util_opt_print_parse_error(opt, argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
warnx("--file is required, see --help for more information");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (action_id == ACTION_NONE) {
|
||||
warnx("One of the following actions must be specified: "
|
||||
"--lock, --release, --lock-and-release");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Determine which util_lockfile function to call */
|
||||
switch (action_id) {
|
||||
case ACTION_LOCK:
|
||||
rc = util_lockfile_parent_lock(path, retries);
|
||||
switch (rc) {
|
||||
case UTIL_LOCKFILE_OK:
|
||||
printf("lock acquired successfully\n");
|
||||
break;
|
||||
case UTIL_LOCKFILE_LOCK_FAIL:
|
||||
warnx("lock was not acquired; already held");
|
||||
return EXIT_FAILURE;
|
||||
case UTIL_LOCKFILE_ERR:
|
||||
warnx("lock was not acquired; file error");
|
||||
return EXIT_FAILURE;
|
||||
default:
|
||||
warnx("Unknown util_lockfile rc %d", rc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
case ACTION_RELEASE:
|
||||
rc = util_lockfile_parent_release(path);
|
||||
switch (rc) {
|
||||
case UTIL_LOCKFILE_OK:
|
||||
printf("lock released successfully\n");
|
||||
break;
|
||||
case UTIL_LOCKFILE_RELEASE_NONE:
|
||||
warnx("lock file did not exist");
|
||||
return EXIT_FAILURE;
|
||||
case UTIL_LOCKFILE_RELEASE_FAIL:
|
||||
warnx("lock was not held by the specified pid");
|
||||
return EXIT_FAILURE;
|
||||
case UTIL_LOCKFILE_ERR:
|
||||
warnx("lock was not released; file error");
|
||||
return EXIT_FAILURE;
|
||||
default:
|
||||
warnx("Unknown util_lockfile rc %d", rc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
case ACTION_LOCK_AND_RELEASE:
|
||||
rc = util_lockfile_lock(path, retries);
|
||||
switch (rc) {
|
||||
case UTIL_LOCKFILE_OK:
|
||||
printf("lock acquired successfully, holding for 2 seconds\n");
|
||||
break;
|
||||
case UTIL_LOCKFILE_LOCK_FAIL:
|
||||
warnx("lock was not acquired; already held");
|
||||
return EXIT_FAILURE;
|
||||
case UTIL_LOCKFILE_ERR:
|
||||
warnx("lock was not acquired; file error");
|
||||
return EXIT_FAILURE;
|
||||
default:
|
||||
warnx("Unknown util_lockfile rc %d", rc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* Briefly sleep while holding the lock */
|
||||
sleep(2);
|
||||
rc = util_lockfile_release(path);
|
||||
switch (rc) {
|
||||
case UTIL_LOCKFILE_OK:
|
||||
printf("lock released successfully\n");
|
||||
break;
|
||||
case UTIL_LOCKFILE_RELEASE_NONE:
|
||||
warnx("lock file did not exist");
|
||||
return EXIT_FAILURE;
|
||||
case UTIL_LOCKFILE_RELEASE_FAIL:
|
||||
warnx("lock was not held by the specified pid");
|
||||
return EXIT_FAILURE;
|
||||
case UTIL_LOCKFILE_ERR:
|
||||
warnx("lock was not released; file error");
|
||||
return EXIT_FAILURE;
|
||||
default:
|
||||
warnx("Unknown util_lockfile rc %d", rc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
warnx("Unknown util_lockfile action");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
//! [code]
|
||||
@@ -171,9 +171,9 @@ int arcb_v1_add_key_slot(arcb_v1_t *arcb, EVP_PKEY *evp_host, GError **error)
|
||||
g_assert(g_bytes_get_size(phkh) == sizeof(key_slot->phkh));
|
||||
|
||||
key_slot = g_malloc0(sizeof(*key_slot));
|
||||
pv_gbytes_memcpy(key_slot->warpk, sizeof(key_slot->warpk), warpk);
|
||||
pv_gbytes_memcpy(key_slot->kst, sizeof(key_slot->warpk), tag);
|
||||
pv_gbytes_memcpy(key_slot->phkh, sizeof(key_slot->warpk), phkh);
|
||||
pv_gbytes_memcpy(key_slot->warpk, sizeof(key_slot->warpk), warpk, NULL);
|
||||
pv_gbytes_memcpy(key_slot->kst, sizeof(key_slot->warpk), tag, NULL);
|
||||
pv_gbytes_memcpy(key_slot->phkh, sizeof(key_slot->warpk), phkh, NULL);
|
||||
|
||||
arcb->host_key_slots = g_slist_prepend(arcb->host_key_slots, g_steal_pointer(&key_slot));
|
||||
return 0;
|
||||
@@ -246,7 +246,7 @@ GBytes *arcb_v1_serialize(const arcb_v1_t *arcb, GError **error)
|
||||
/* copy plain data to contiguous memory */
|
||||
hdr.arl = GUINT32_TO_BE((uint32_t)att_req_len);
|
||||
|
||||
pv_gbytes_memcpy(hdr.iv, ARCB_V1_IV_SIZE, arcb->iv);
|
||||
pv_gbytes_memcpy(hdr.iv, ARCB_V1_IV_SIZE, arcb->iv, NULL);
|
||||
hdr.nks = (uint8_t)nks;
|
||||
hdr.sea = GUINT32_TO_BE((uint32_t)sea);
|
||||
ecdh_cpk = pv_evp_pkey_to_ecdh_pub_key(arcb->evp_cust_pub_key, error);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "libpv/crypto.h"
|
||||
#include "libpv/macros.h"
|
||||
|
||||
#include "types.h"
|
||||
|
||||
@@ -28,18 +29,17 @@
|
||||
#define ARCB_V1_IV_SIZE 12
|
||||
#define ARCB_V1_PHKH_SIZE 32
|
||||
|
||||
#define BIT(bit) ((uint64_t)1 << (63 - (bit)))
|
||||
/* Optional nonce in ARCB */
|
||||
#define ARCB_V1_PAF_NONCE BIT(1)
|
||||
#define ARCB_V1_PAF_NONCE PV_MSB(1)
|
||||
/* Public host key hash used to unseal SE header added to additional data to be measured */
|
||||
#define ARCB_V1_PAF_AAD_PHKH_HEADER BIT(2)
|
||||
#define ARCB_V1_PAF_AAD_PHKH_HEADER PV_MSB(2)
|
||||
/* Public host key hash used to unseal this attestation added to additional data to be measured */
|
||||
#define ARCB_V1_PAF_AAD_PHKH_ATTEST BIT(3)
|
||||
#define ARCB_V1_PAF_AAD_PHKH_ATTEST PV_MSB(3)
|
||||
/* Temporary backup-host-key use allowed */
|
||||
#define ARCB_V1_PAF_TMP_BACKUP_ALLOWED BIT(62)
|
||||
#define ARCB_V1_PAF_TMP_BACKUP_ALLOWED PV_MSB(62)
|
||||
|
||||
/* Global not-host-specific key allowed */
|
||||
#define ARCB_V1_PAF_GLOBAL_NHS_KEY_ALLOWED BIT(63)
|
||||
#define ARCB_V1_PAF_GLOBAL_NHS_KEY_ALLOWED PV_MSB(63)
|
||||
|
||||
#define ARCB_V1_PAF_ALL \
|
||||
(ARCB_V1_PAF_NONCE | ARCB_V1_PAF_AAD_PHKH_HEADER | ARCB_V1_PAF_AAD_PHKH_ATTEST | \
|
||||
|
||||
@@ -130,7 +130,7 @@ void att_add_uid(att_meas_ctx_t *meas_ctx, GBytes *config_uid)
|
||||
pv_wrapped_g_assert(config_uid);
|
||||
|
||||
g_assert(g_bytes_get_size(config_uid) == ATT_CONFIG_UID_SIZE);
|
||||
pv_gbytes_memcpy(meas_ctx->config_uid, ATT_CONFIG_UID_SIZE, config_uid);
|
||||
pv_gbytes_memcpy(meas_ctx->config_uid, ATT_CONFIG_UID_SIZE, config_uid, NULL);
|
||||
}
|
||||
|
||||
gboolean att_verify_measurement(const GBytes *calculated_measurement,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/* Must be included before any other header */
|
||||
#include "config.h"
|
||||
|
||||
#include <glib/gi18n-lib.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libpv/glib-helper.h"
|
||||
|
||||
@@ -191,7 +191,7 @@ exchange_format_ctx_t *exchange_ctx_from_file(const char *filename, GError **err
|
||||
}
|
||||
ctx->req_meas_size = GUINT32_FROM_BE(hdr_v1->measurement.size);
|
||||
ctx->req_add_size = GUINT32_FROM_BE(hdr_v1->additional_data.size);
|
||||
ctx->version = GUINT32_TO_BE(hdr->version);
|
||||
ctx->version = GUINT32_FROM_BE(hdr->version);
|
||||
|
||||
return g_steal_pointer(&ctx);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
/* Must be included before any other header */
|
||||
#include "config.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#ifdef PVATTEST_COMPILE_PERFORM
|
||||
#include <stdio.h>
|
||||
@@ -42,26 +43,29 @@ uvio_attest_t *build_attestation_v1_ioctl(GBytes *serialized_arcb, GBytes *user_
|
||||
uvio_attest = g_malloc0(sizeof(*uvio_attest));
|
||||
uvio_attest->arcb_addr = PTR_TO_U64(g_steal_pointer(&arcb));
|
||||
g_assert_cmpuint(arcb_size, <, UINT32_MAX);
|
||||
uvio_attest->arcb_len = GUINT32_TO_BE((uint32_t)arcb_size);
|
||||
uvio_attest->arcb_len = (uint32_t)arcb_size;
|
||||
/* transferred the local ownership of the arcb from this function to uvio_attest; nullify pointer */
|
||||
g_steal_pointer(&serialized_arcb);
|
||||
|
||||
if (user_data) {
|
||||
if (g_bytes_get_size(user_data) > sizeof(uvio_attest->user_data)) {
|
||||
size_t copied_data_size;
|
||||
|
||||
if (pv_gbytes_memcpy(uvio_attest->user_data, sizeof(uvio_attest->user_data),
|
||||
user_data, &copied_data_size) == NULL) {
|
||||
g_set_error(error, ATT_ERROR, ATT_ERR_INVALID_USER_DATA,
|
||||
_("User data larger than %li bytes"),
|
||||
sizeof(uvio_attest->user_data));
|
||||
_("User data %li bytes is larger than %li bytes"),
|
||||
g_bytes_get_size(user_data), sizeof(uvio_attest->user_data));
|
||||
return NULL;
|
||||
}
|
||||
uvio_attest->user_data_len = GUINT16_TO_BE((uint16_t)g_bytes_get_size(user_data));
|
||||
pv_gbytes_memcpy(uvio_attest->user_data, uvio_attest->user_data_len, user_data);
|
||||
STATIC_ASSERT(sizeof(uvio_attest->user_data) <= UINT16_MAX);
|
||||
uvio_attest->user_data_len = (uint16_t)copied_data_size;
|
||||
}
|
||||
|
||||
uvio_attest->meas_len = GUINT32_TO_BE(measurement_size);
|
||||
uvio_attest->meas_addr = PTR_TO_U64(g_malloc0(uvio_attest->meas_len));
|
||||
uvio_attest->meas_addr = PTR_TO_U64(g_malloc0(measurement_size));
|
||||
uvio_attest->meas_len = measurement_size;
|
||||
|
||||
uvio_attest->add_data_len = GUINT32_TO_BE(add_data_size);
|
||||
uvio_attest->add_data_addr = PTR_TO_U64(g_malloc0(uvio_attest->add_data_len));
|
||||
uvio_attest->add_data_addr = PTR_TO_U64(g_malloc0(add_data_size));
|
||||
uvio_attest->add_data_len = add_data_size;
|
||||
|
||||
return g_steal_pointer(&uvio_attest);
|
||||
}
|
||||
@@ -83,7 +87,7 @@ GBytes *uvio_get_measurement(const uvio_attest_t *attest)
|
||||
|
||||
if (attest->meas_addr == (__u64)0)
|
||||
return NULL;
|
||||
return g_bytes_new(U64_TO_PTR(attest->meas_addr), GUINT32_FROM_BE(attest->meas_len));
|
||||
return g_bytes_new(U64_TO_PTR(attest->meas_addr), attest->meas_len);
|
||||
}
|
||||
|
||||
GBytes *uvio_get_additional_data(const uvio_attest_t *attest)
|
||||
@@ -92,8 +96,7 @@ GBytes *uvio_get_additional_data(const uvio_attest_t *attest)
|
||||
|
||||
if (attest->add_data_addr == (__u64)0)
|
||||
return NULL;
|
||||
return g_bytes_new(U64_TO_PTR(attest->add_data_addr),
|
||||
GUINT32_FROM_BE(attest->add_data_len));
|
||||
return g_bytes_new(U64_TO_PTR(attest->add_data_addr), attest->add_data_len);
|
||||
}
|
||||
|
||||
GBytes *uvio_get_config_uid(const uvio_attest_t *attest)
|
||||
@@ -127,7 +130,7 @@ uint16_t uvio_ioctl(const int uv_fd, const unsigned int cmd, const uint32_t flag
|
||||
g_set_error(error, UVIO_ERROR, UVIO_ERR_UV_NOT_OK,
|
||||
_("Ultravisor call returned '%#x' (%s)"), uv_ioctl->uv_rc,
|
||||
uvio_uv_rc_to_str(uv_ioctl->uv_rc));
|
||||
return GUINT16_FROM_BE(uv_ioctl->uv_rc);
|
||||
return uv_ioctl->uv_rc;
|
||||
}
|
||||
|
||||
uint16_t uvio_ioctl_attest(const int uv_fd, uvio_attest_t *attest, GError **error)
|
||||
|
||||
@@ -17,7 +17,8 @@ export LC_ALL
|
||||
readonly SCRIPTNAME="${0##*/}" # general name of this script
|
||||
#
|
||||
readonly DATETIME="$(date +%Y-%m-%d-%H-%M-%S 2>/dev/null)"
|
||||
readonly DOCKER=$(if type -t docker >/dev/null; then echo "YES"; else echo "NO"; fi)
|
||||
readonly DOCKER=$(if type docker >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
|
||||
readonly DUMP2TAR_OK=$(if type dump2tar >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
|
||||
readonly HW="$(uname -i 2>/dev/null)"
|
||||
# retrieve and split kernel version
|
||||
readonly KERNEL_BASE="$(uname -r 2>/dev/null)"
|
||||
@@ -25,7 +26,9 @@ readonly KERNEL_VERSION=$(echo ${KERNEL_BASE} | cut -d'.' -f1 )
|
||||
readonly KERNEL_MAJOR_REVISION=$(echo ${KERNEL_BASE} | cut -d'.' -f2 )
|
||||
readonly KERNEL_MINOR_REVISION=$(echo ${KERNEL_BASE} | cut -d'.' -f3 | sed 's/[^0-9].*//g')
|
||||
readonly KERNEL_INFO=${KERNEL_VERSION}.${KERNEL_MAJOR_REVISION}.${KERNEL_MINOR_REVISION}
|
||||
readonly KVM=$(if type -t virsh >/dev/null; then echo "YES"; else echo "NO"; fi)
|
||||
readonly KUBERNETES=$(if type kubectl >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
|
||||
readonly KUBERNETES_LOG_LINES=50
|
||||
readonly KVM=$(if type virsh >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
|
||||
# The file to indicate that another instance of the script is already running
|
||||
readonly LOCKFILE="/tmp/${SCRIPTNAME}.lock"
|
||||
# check limits for logfiles like /var/log/messages
|
||||
@@ -43,14 +46,20 @@ readonly PROCESSORVERSION="$(grep -E ".*processor 0:.*" /proc/cpuinfo | \
|
||||
sed 's/.*version[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g')"
|
||||
if test "x${PROCESSORVERSION}" = "xFF" || test "x${PROCESSORVERSION}" = "xff"; then
|
||||
RUNTIME_ENVIRONMENT=$(grep -E "VM00.*Control Program.*" /proc/sysinfo | \
|
||||
sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g')
|
||||
sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g')
|
||||
else
|
||||
RUNTIME_ENVIRONMENT="LPAR"
|
||||
fi
|
||||
readonly SYSTEMHOSTNAME="$(hostname -s 2>/dev/null)" # hostname of system being analysed
|
||||
readonly TERMINAL="$(tty 2>/dev/null)"
|
||||
# The processor version for the first processor and resulting vitrtualization RUNTIME
|
||||
readonly TOS=15 # timeout seconds for command execution
|
||||
# timeout seconds TOS / kill timeout TOKS
|
||||
readonly TOS=15
|
||||
readonly TOKS=30
|
||||
readonly TIMEOUT=$(if type timeout >/dev/null 2>&1; then echo "YES"; else echo "NO"; fi)
|
||||
readonly TIMEOUT_OK=$(if test "x${TIMEOUT}" = "xYES"; then
|
||||
if test $(timeout -k ${TOKS} ${TOS} uname 2>/dev/null) = $(uname); then
|
||||
echo "YES"; else echo "WRONG_VERSION"; fi
|
||||
else echo "NO"; fi)
|
||||
readonly ZDEV_CONF=$(lszdev --configured 2>/dev/null | wc -l)
|
||||
readonly ZDEV_OFF=$(lszdev --offline 2>/dev/null | wc -l)
|
||||
readonly ZDEV_ONL=$(lszdev --online 2>/dev/null | wc -l)
|
||||
@@ -79,7 +88,7 @@ This script collects runtime, configuration and trace information on
|
||||
a Linux on IBM Z installation for debugging purposes.
|
||||
|
||||
It also traces information about z/VM if the Linux runs under z/VM.
|
||||
KVM or DOCKER data ist collected on a host serving this.
|
||||
Virtualization platform data is collected on a host serving this.
|
||||
|
||||
Default location for data collection and final tar file is "/tmp/".
|
||||
The collected information is written to a TAR archive named
|
||||
@@ -96,7 +105,7 @@ Options:
|
||||
stores the temporary data and the final archive.
|
||||
-h|--help print this help
|
||||
-v|--version print version information
|
||||
-c|--check online quick check (no data collection)
|
||||
-c|--check online base system information (no data collection)
|
||||
|
||||
Please report bugs to: linux390@de.ibm.com
|
||||
|
||||
@@ -150,7 +159,7 @@ $(cat /proc/sysinfo | grep 'Name')
|
||||
Kernel version = ${KERNEL_INFO}
|
||||
OS version / distro = ${OS_NAME}
|
||||
KVM host = ${KVM}
|
||||
DOCKER host = ${DOCKER}
|
||||
container host Kubernetes: ${KUBERNETES} - docker: ${DOCKER}
|
||||
|
||||
Current user = $(whoami) (must be root for data collection)
|
||||
Date and time = $(date)
|
||||
@@ -162,6 +171,9 @@ Log file check =$(logfile_checker "/var/log*")
|
||||
Working directory = $(ls -d ${paramWORKDIR_BASE} 2>&1 && df -k ${paramWORKDIR_BASE})
|
||||
$(ls -ltr ${paramWORKDIR_BASE}/DBGINFO*tgz 2>/dev/null | tail -2)
|
||||
$(ls ${LOCKFILE} 2>/dev/null && echo " Warning: dbginfo running since: $(cat ${LOCKFILE})")
|
||||
Tool/command dependency check successful:
|
||||
timeout = ${TIMEOUT_OK}
|
||||
dump2tar = ${DUMP2TAR_OK}
|
||||
|
||||
This is a console output only - no data was saved using option -c !
|
||||
|
||||
@@ -213,7 +225,8 @@ done
|
||||
|
||||
# finally verification to run as root
|
||||
if test "$(/usr/bin/id -u 2>/dev/null)" -ne 0; then
|
||||
echo "${SCRIPTNAME}: Error: You must be user root to run \"${SCRIPTNAME}\"!"
|
||||
echo "${SCRIPTNAME}: Error: You must be user root to run data collection \"${SCRIPTNAME}\"!"
|
||||
echo "${SCRIPTNAME}: Hint: for base system information you may run \"${SCRIPTNAME} -c\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -249,6 +262,7 @@ readonly OUTPUT_FILE_ISW="${WORKPATH}installed_sw.out"
|
||||
readonly OUTPUT_FILE_TC="${WORKPATH}network.out"
|
||||
readonly OUTPUT_FILE_VMCMD="${WORKPATH}zvm_runtime.out"
|
||||
# Base file names for different output files - no extension !
|
||||
readonly OUTPUT_FILE_KUBERNETES="${WORKPATH}kubernetes"
|
||||
readonly OUTPUT_FILE_OSAOAT="${WORKPATH}network"
|
||||
readonly OUTPUT_FILE_SYSFS="${WORKPATH}sysfs"
|
||||
|
||||
@@ -268,7 +282,7 @@ ALL_STEPS="\
|
||||
collect_bridge\
|
||||
collect_ovs\
|
||||
collect_kvm\
|
||||
collect_docker\
|
||||
collect_container\
|
||||
collect_nvme\
|
||||
collect_logfiles\
|
||||
post_processing\
|
||||
@@ -329,14 +343,18 @@ fi
|
||||
########################################
|
||||
|
||||
LOGFILES="\
|
||||
/root/anaconda-ks.cfg\
|
||||
/root/original-ks.cfg\
|
||||
/run/containerd/events.log\
|
||||
/run/docker/libcontainerd/containerd/events.log\
|
||||
/run/udev/chreiplzfcpmp-[0-9][0-9][a-z][a-z][a-z][a-z]-*\
|
||||
/var/log/anaconda\
|
||||
/var/log/anaconda.*\
|
||||
/var/log/audit\
|
||||
/var/log/boot*\
|
||||
/var/log/cron*\
|
||||
/var/log/dmesg*\
|
||||
/var/log/dnf.*\
|
||||
/var/log/dracut.log*\
|
||||
/var/log/IBMtape.trace\
|
||||
/var/log/IBMtape.errorlog\
|
||||
@@ -346,9 +364,12 @@ LOGFILES="\
|
||||
/var/log/messages*\
|
||||
/var/log/openvswitch/ovs-vswitchd.log\
|
||||
/var/log/openvswitch/ovsdb-server.log\
|
||||
/var/log/pbl.log\
|
||||
/var/log/sa\
|
||||
/var/log/syslog*\
|
||||
/var/log/yum.log\
|
||||
/var/log/YaST2\
|
||||
/var/log/zypp*\
|
||||
"
|
||||
|
||||
########################################
|
||||
@@ -368,6 +389,7 @@ CONFIGFILES="\
|
||||
/etc/crypttab\
|
||||
/etc/default\
|
||||
/etc/depmod.d\
|
||||
/etc/dnf/dnf.conf\
|
||||
/etc/docker\
|
||||
/etc/dracut.conf.d\
|
||||
/etc/exports\
|
||||
@@ -468,6 +490,7 @@ CMDS="${CMDS}\
|
||||
:dmsetup table --target multipath\
|
||||
:dmsetup status\
|
||||
:multipathd -k'show config'\
|
||||
:multipathd -k'show config local'\
|
||||
:multipathd -k'show maps'\
|
||||
:multipathd -k'show topo'\
|
||||
:multipathd -k'show paths'\
|
||||
@@ -557,6 +580,18 @@ DOCKER_CMDS="docker version\
|
||||
:systemctl status docker.service\
|
||||
"
|
||||
|
||||
########################################
|
||||
KUBERNETES_CMDS="kubectl version\
|
||||
:oc version # get also OCP version if installed\
|
||||
:kubectl top pod\
|
||||
:kubectl top node\
|
||||
:kubectl describe pod -A\
|
||||
:kubectl describe node -A\
|
||||
:kubectl describe endpoints\
|
||||
:ls -l /var/log/containers # list of logs is sufficent\
|
||||
:kubectl cluster-info dump >>${OUTPUT_FILE_KUBERNETES}.dmp\
|
||||
"
|
||||
|
||||
########################################
|
||||
VM_CMDS="q userid\
|
||||
:q users\
|
||||
@@ -676,18 +711,18 @@ collect_vmcmdsout() {
|
||||
local vm_userid
|
||||
local module_loaded=1
|
||||
local ifs_orig="${IFS}"
|
||||
local cp_buffer_size=2
|
||||
local rc_buffer_size=2
|
||||
local cp_buffer_size
|
||||
local rc_buffer_check
|
||||
|
||||
if echo "${RUNTIME_ENVIRONMENT}" | grep -qi "z/VM" >/dev/null 2>&1; then
|
||||
pr_collect_output "z/VM"
|
||||
|
||||
if type -t vmcp >/dev/null; then
|
||||
if type vmcp >/dev/null; then
|
||||
cp_command="vmcp"
|
||||
if ! lsmod 2>/dev/null | grep -q vmcp && modinfo vmcp >/dev/null 2>&1; then
|
||||
modprobe vmcp && module_loaded=0 && sleep 2
|
||||
fi
|
||||
elif type -t hcp >/dev/null; then
|
||||
elif type hcp >/dev/null; then
|
||||
cp_command="hcp"
|
||||
if ! lsmod 2>/dev/null | grep -q cpint; then
|
||||
modprobe cpint && module_loaded=0 && sleep 2
|
||||
@@ -703,11 +738,14 @@ collect_vmcmdsout() {
|
||||
IFS=:
|
||||
for vm_command in ${vm_cmds}; do
|
||||
IFS="${ifs_orig}"
|
||||
while test ${rc_buffer_size} -eq 2 && test ${cp_buffer_size} -lt 1024; do
|
||||
# initialize buffer values for automatic resizing
|
||||
cp_buffer_size=2
|
||||
rc_buffer_check=2
|
||||
while test ${rc_buffer_check} -eq 2 && test ${cp_buffer_size} -lt 1024; do
|
||||
cp_buffer_size=$(( cp_buffer_size * 2 ))
|
||||
eval ${cp_command} -b ${cp_buffer_size}k "${vm_command}" \
|
||||
>/dev/null 2>&1
|
||||
rc_buffer_size=$?
|
||||
rc_buffer_check=$?
|
||||
done
|
||||
call_run_command "${cp_command} -b ${cp_buffer_size}k ${vm_command}" \
|
||||
"${OUTPUT_FILE_VMCMD}"
|
||||
@@ -781,7 +819,7 @@ collect_sysfs() {
|
||||
# files known to block on read (-x). Stop reading a file that takes
|
||||
# more than 5 seconds (-T 5) such as an active ftrace buffer.
|
||||
# error messages are not written to the log
|
||||
if type -t dump2tar >/dev/null; then
|
||||
if test "x${DUMP2TAR_OK}" = "xYES"; then
|
||||
dump2tar /sys -z -o "${OUTPUT_FILE_SYSFS}.tgz" \
|
||||
-x '*/tracing/trace_pipe*' \
|
||||
-x '*/page_idle/bitmap*' \
|
||||
@@ -859,7 +897,7 @@ collect_osaoat() {
|
||||
|
||||
network_devices=$(lsqeth 2>/dev/null | grep "Device name" \
|
||||
| sed 's/D.*:[[:space:]]*\([^[:space:]]*\)[[:space:]]\+/\1/g')
|
||||
if type -t qethqoat >/dev/null; then
|
||||
if type qethqoat >/dev/null; then
|
||||
if test -n "${network_devices}"; then
|
||||
pr_collect_output "osa oat"
|
||||
for network_device in ${network_devices}; do
|
||||
@@ -882,7 +920,7 @@ collect_ethtool() {
|
||||
local network_device
|
||||
|
||||
network_devices=$(ls /sys/class/net 2>/dev/null)
|
||||
if type -t ethtool >/dev/null; then
|
||||
if type ethtool >/dev/null; then
|
||||
if test -n "${network_devices}"; then
|
||||
pr_collect_output "ethtool"
|
||||
for network_device in ${network_devices}; do
|
||||
@@ -923,7 +961,7 @@ collect_tc() {
|
||||
local network_device
|
||||
|
||||
network_devices=$(ls /sys/class/net 2>/dev/null)
|
||||
if type -t tc >/dev/null; then
|
||||
if type tc >/dev/null; then
|
||||
if test -n "${network_devices}"; then
|
||||
pr_collect_output "Trafic Control"
|
||||
for network_device in ${network_devices}; do
|
||||
@@ -944,7 +982,7 @@ collect_bridge() {
|
||||
local network_device
|
||||
|
||||
network_devices=$(ls /sys/class/net 2>/dev/null)
|
||||
if type -t bridge >/dev/null; then
|
||||
if type bridge >/dev/null; then
|
||||
if test -n "${network_devices}"; then
|
||||
pr_collect_output "bridge"
|
||||
for network_device in ${network_devices}; do
|
||||
@@ -976,7 +1014,7 @@ collect_ovs() {
|
||||
:ovs-vsctl -t 5 show\
|
||||
:ovsdb-client dump\
|
||||
"
|
||||
if type -t ovs-vsctl >/dev/null; then
|
||||
if type ovs-vsctl >/dev/null; then
|
||||
pr_collect_output "OpenVSwitch"
|
||||
IFS=:
|
||||
for ovscmd in ${ovscmds}; do
|
||||
@@ -1001,14 +1039,21 @@ collect_ovs() {
|
||||
}
|
||||
|
||||
########################################
|
||||
collect_docker() {
|
||||
collect_container() {
|
||||
local container_list
|
||||
local network_list
|
||||
local item
|
||||
|
||||
# check if docker command exists
|
||||
# check if container environment exists
|
||||
if test "x${DOCKER}" = "xYES" || test "x${KUBERNETES}" = "xYES"; then
|
||||
pr_collect_output "container host"
|
||||
else
|
||||
pr_skip "container host: not found"
|
||||
fi
|
||||
|
||||
# for docker command exists
|
||||
if [ "x${DOCKER}" = "xYES" ]; then
|
||||
pr_collect_output "docker"
|
||||
pr_syslog_stdout " docker ..."
|
||||
container_list=$(docker ps -qa)
|
||||
network_list=$(docker network ls -q)
|
||||
ifs_orig="${IFS}"
|
||||
@@ -1030,8 +1075,25 @@ collect_docker() {
|
||||
"${OUTPUT_FILE_DOCKER}"
|
||||
done
|
||||
fi
|
||||
else
|
||||
pr_skip "docker: not available"
|
||||
fi
|
||||
|
||||
# for kubectl command exists
|
||||
if [ "x${KUBERNETES}" = "xYES" ]; then
|
||||
pr_syslog_stdout " Kubernetes ..."
|
||||
container_list=$(kubectl top pod | grep -v "MEMORY(bytes)" | cut -d' ' -f1)
|
||||
|
||||
ifs_orig="${IFS}"
|
||||
IFS=:
|
||||
for item in ${KUBERNETES_CMDS}; do
|
||||
IFS=${ifs_orig} call_run_command "${item}" "${OUTPUT_FILE_KUBERNETES}.out"
|
||||
done
|
||||
IFS="${ifs_orig}"
|
||||
if test -n "${container_list}"; then
|
||||
for item in ${container_list}; do
|
||||
call_run_command "kubectl logs --tail=${KUBERNETES_LOG_LINES} ${item}"\
|
||||
"${OUTPUT_FILE_KUBERNETES}.log"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1039,7 +1101,7 @@ collect_docker() {
|
||||
collect_nvme() {
|
||||
local device
|
||||
|
||||
if type -t nvme >/dev/null; then
|
||||
if type nvme >/dev/null; then
|
||||
pr_collect_output "NVME storage"
|
||||
call_run_command "nvme list" "${OUTPUT_FILE_NVME}"
|
||||
for device in /dev/nvme[0-9]*; do
|
||||
@@ -1080,7 +1142,7 @@ collect_kvm() {
|
||||
call_run_command "virsh domstats ${domain}" "${OUTPUT_FILE_KVM}"
|
||||
done
|
||||
else
|
||||
echo "no KVM doamins found" | tee -a ${OUTPUT_FILE_KVM}
|
||||
echo "no KVM domains found" | tee -a ${OUTPUT_FILE_KVM}
|
||||
fi
|
||||
else
|
||||
pr_skip "KVM: no virsh command"
|
||||
@@ -1139,23 +1201,23 @@ call_run_command() {
|
||||
local rc=0
|
||||
local cmd="${1}"
|
||||
local logfile="${2}"
|
||||
# extract the raw_command and set cmd_type
|
||||
local raw_cmd=$(echo "${cmd}" | sed -ne 's/^\([^[:space:]]*\).*$/\1/p')
|
||||
local cmd_type=$(type -t ${raw_cmd})
|
||||
# timeout_ok - like a boolean - is not empty if command exists
|
||||
local timeout_ok=$(type -t timeout)
|
||||
# extract the raw_command and set cmd_type, as some shell might split into
|
||||
# several lines restrict the cmd echo to the first line only
|
||||
local raw_cmd=$(echo "${cmd}" | head -1 | sed -ne 's/^\([^[:space:]]*\).*$/\1/p')
|
||||
local cmd_type=$(type ${raw_cmd} | cut -d' ' -sf4,5)
|
||||
|
||||
echo "#######################################################" >> "${logfile}"
|
||||
echo "${USER}@${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}"
|
||||
|
||||
# check calling command type
|
||||
if [ "X${cmd_type}" = "Xbuiltin" ]; then
|
||||
if [ "X${cmd_type}" = "Xshell builtin" ]; then
|
||||
# command is a builtin (no use of timeout possible)
|
||||
eval "${cmd}" >> ${logfile} 2>&1
|
||||
rc=$?
|
||||
elif [ "X${cmd_type}" != "X" ]; then
|
||||
if [ "X${timeout_ok}" = "Xfile" ]; then
|
||||
eval timeout -k ${TOS} ${TOS} "${cmd}" >> ${logfile} 2>&1
|
||||
elif [ "X${cmd_type}" != "Xnot found" ]; then
|
||||
if [ "x${TIMEOUT_OK}" = "xYES" ]; then
|
||||
eval timeout -k ${TOKS} ${TOS} "${cmd}" >> ${logfile} 2>&1
|
||||
|
||||
rc=$?
|
||||
else
|
||||
# fall back - call all existing commands without timeout
|
||||
@@ -1347,6 +1409,9 @@ print_version
|
||||
# saving stdout/stderr and redirecting stdout/stderr into log file
|
||||
exec 8>&1 9>&2 >"${LOGFILE}" 2>&1
|
||||
|
||||
# run print_version again to be logged in dbginfo.log
|
||||
print_version
|
||||
|
||||
# trap on SIGHUP=1 SIGINT=2 SIGTERM=15
|
||||
trap emergency_exit SIGHUP SIGINT SIGTERM
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH DBGINFO.SH 8 "May 2022" "s390-tools"
|
||||
.TH DBGINFO.SH 8 "Sep 2022" "s390-tools"
|
||||
|
||||
.SH NAME
|
||||
dbginfo.sh \- collect runtime, configuration and trace information
|
||||
@@ -8,13 +8,13 @@ for debugging Linux on IBM Z
|
||||
.br
|
||||
\fBdbginfo.sh\fP [OPTIONS]
|
||||
.br
|
||||
\fBdbginfo.sh\fP {\-h|\-v}
|
||||
\fBdbginfo.sh\fP {\-h|\-c|\-v}
|
||||
|
||||
.SH DESCRIPTION
|
||||
This script collects runtime, configuration and trace information that can
|
||||
be used to debug a Linux on IBM Z instance.
|
||||
For Linux on z/VM, the script also traces information about the z/VM system.
|
||||
KVM or DOCKER data ist collected on a host serving this.
|
||||
Virtualization platform data is collected on a host serving this.
|
||||
|
||||
The debug information is written to a file
|
||||
/<DIRECTORY>/DBGINFO\-<date>\-<time>\-<hostname>\-<processorid>.tgz
|
||||
@@ -59,7 +59,7 @@ Sample invocation:
|
||||
.br
|
||||
dbginfo.sh: Debug information script version %S390_TOOLS_VERSION%
|
||||
.br
|
||||
Copyright IBM Corp. 2002, 2021
|
||||
Copyright IBM Corp. 2002, 2022
|
||||
.PP
|
||||
Hardware platform = s390x
|
||||
.br
|
||||
@@ -97,8 +97,10 @@ Date and time of info = 2021-08-26-15-27-58
|
||||
.PP
|
||||
13 of 18: Skip KVM: no virsh command
|
||||
.PP
|
||||
14 of 18: Collecting docker output
|
||||
14 of 18: Collecting container host output
|
||||
.PP
|
||||
Kubernetes ...
|
||||
.br
|
||||
15 of 18: Collecting NVME storage output
|
||||
.PP
|
||||
16 of 18: Collecting log files
|
||||
|
||||
@@ -872,7 +872,7 @@ static void show_devices_argv(char *argv[])
|
||||
show_device(rec, card);
|
||||
free(card);
|
||||
}
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
if (sscanf(argv[i]+1, "%x", &dom) == 1) {
|
||||
/* list specific domains of all adapters */
|
||||
@@ -897,7 +897,7 @@ static void show_devices_argv(char *argv[])
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
printf("Invalid adpater id!\n");
|
||||
}
|
||||
|
||||
@@ -55,9 +55,22 @@ install() {
|
||||
chzdev --export - --persistent --by-attrib "zdev:early=1" --quiet \
|
||||
--type 2>/dev/null >> "$_tempfile"
|
||||
|
||||
# Obtain early device configuration for site-specific settings
|
||||
for (( site=0; site<10; site++ ))
|
||||
do
|
||||
chzdev --export - --persistent --by-attrib "zdev:early=1" --site $site \
|
||||
--quiet 2>/dev/null >> "$_tempfile"
|
||||
done
|
||||
|
||||
# Apply via --import to prevent other devices from being configured
|
||||
chzdev --import "$_tempfile" --persistent --base "/etc=$initdir/etc" \
|
||||
--yes --quiet --no-root-update --force >/dev/null
|
||||
# Apply site-specific configurations via --import
|
||||
for (( site=0; site<10; site++ ))
|
||||
do
|
||||
chzdev --import "$_tempfile" --persistent --base "/etc=$initdir/etc" \
|
||||
--site $site --yes --quiet --no-root-update --force >/dev/null
|
||||
done
|
||||
|
||||
rm -f "$_tempfile"
|
||||
|
||||
|
||||
@@ -19,6 +19,20 @@
|
||||
struct subtype;
|
||||
struct setting_list;
|
||||
|
||||
/**
|
||||
* Currently zdev supports 10 sites. Which means, zdev support 10 different
|
||||
* set of attributes which are specific to each site. When the user does
|
||||
* not provide any site information, the common set will be used which is
|
||||
* not specific to any site. So, total we have 11 persistent attribute sets
|
||||
* Where,
|
||||
* 0- 9: Site specific attributes
|
||||
* 10: Common attributes which do not belong to any sites
|
||||
*/
|
||||
|
||||
#define NUM_SITES 11
|
||||
#define NUM_USER_SITES (NUM_SITES - 1)
|
||||
#define SITE_FALLBACK NUM_USER_SITES
|
||||
|
||||
/**
|
||||
* struct device_state - Represent the state of a device in a configuration
|
||||
* @settings: List of attribute settings
|
||||
@@ -61,6 +75,7 @@ struct device {
|
||||
struct device_state active;
|
||||
struct device_state persistent;
|
||||
struct device_state autoconf;
|
||||
struct device_state site_specific[NUM_SITES];
|
||||
|
||||
unsigned int processed:1;
|
||||
};
|
||||
@@ -97,8 +112,11 @@ struct device *device_list_find(struct device_list *, const char *,
|
||||
struct device *);
|
||||
void device_list_print(struct device_list *, int);
|
||||
struct setting_list *device_get_setting_list(struct device *dev,
|
||||
config_t config);
|
||||
config_t config,
|
||||
int site_id);
|
||||
|
||||
config_t device_get_config(struct device *dev);
|
||||
char *device_get_sites(struct device *dev);
|
||||
bool is_dev_pers(struct device *dev);
|
||||
|
||||
#endif /* DEVICE_H */
|
||||
|
||||
@@ -38,6 +38,7 @@ extern struct devtype *devtypes[];
|
||||
* devtypes with empty title will not be shown with --list-types.
|
||||
* @devname: Short name for devices of this device type
|
||||
* @modules: (Optional) Array of kernel modules required for this devtype
|
||||
* @site_support: Support for site-specific configuration for the device-type
|
||||
* @subtypes: Array of subtypes
|
||||
* @type_attribs: Array of device type attributes (may point to empty array)
|
||||
* @unknown_type_attribs: Allow specification of unknown device type attributes
|
||||
@@ -59,6 +60,7 @@ struct devtype {
|
||||
const char title[DEVTYPE_TITLE_LEN + 1];
|
||||
const char *devname;
|
||||
const char **modules;
|
||||
unsigned int site_support:1;
|
||||
|
||||
struct subtype **subtypes;
|
||||
struct attrib **type_attribs;
|
||||
|
||||
@@ -103,6 +103,8 @@ extern int delayed_warnings;
|
||||
extern unsigned long longrun_total;
|
||||
extern unsigned long longrun_current;
|
||||
|
||||
extern int global_site_id;
|
||||
|
||||
void misc_exit(void);
|
||||
void indent(unsigned int, const char *, ...);
|
||||
void error(const char *, ...);
|
||||
@@ -239,6 +241,7 @@ void longrun_stop(void);
|
||||
char *skip_comp(char *);
|
||||
void byte_swap(uint8_t *, unsigned int *, unsigned);
|
||||
bool valid_hex(const char *);
|
||||
bool is_valid_site(const char *);
|
||||
void debug_init(int, char **);
|
||||
|
||||
#endif /* MISC_H */
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define MODPROBE_PREFIX "s390x"
|
||||
#define UDEV_PREFIX "41"
|
||||
#define UDEV_SUFFIX ".rules"
|
||||
#define ZDEV_PREFIX "40"
|
||||
|
||||
#define PATH_MODPROBE_CONF "/etc/modprobe.d"
|
||||
#define PATH_MODPROBE "/sbin/modprobe"
|
||||
@@ -65,6 +66,7 @@ char *path_get_zfcp_lun_dev(struct zfcp_lun_devid *);
|
||||
char *path_get_zfcp_port_dev(struct zfcp_lun_devid *);
|
||||
char *path_get_scsi_hctl_dev(const char *);
|
||||
char *path_get_bus_attr(const char *, const char *);
|
||||
char *path_get_zdev_rule(const char *name);
|
||||
|
||||
exit_code_t path_for_each(const char *,
|
||||
exit_code_t (*callback)(const char *, const char *,
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
|
||||
#include "exit_code.h"
|
||||
|
||||
exit_code_t initrd_check(bool all_pers);
|
||||
exit_code_t initrd_check(bool all_pers, int site_validate);
|
||||
|
||||
#endif /* ROOT_H */
|
||||
|
||||
@@ -19,6 +19,9 @@ struct setting_list;
|
||||
extern int udev_need_settle;
|
||||
extern int udev_no_settle;
|
||||
|
||||
#define SITE_BLOCK_START "# site_start"
|
||||
#define SITE_BLOCK_END "# site_end"
|
||||
|
||||
bool udev_file_is_empty(struct util_udev_file *file);
|
||||
|
||||
void udev_get_device_ids(const char *type, struct util_list *list,
|
||||
@@ -31,4 +34,7 @@ void udev_add_internal_from_entry(struct setting_list *list,
|
||||
struct util_udev_entry_node *entry,
|
||||
struct attrib **attribs);
|
||||
|
||||
exit_code_t udev_write_site_rule(void);
|
||||
bool is_legacy_rule(struct util_udev_file *file);
|
||||
|
||||
#endif /* UDEV_H */
|
||||
|
||||
15
zdev/include/zdev_id.h
Normal file
15
zdev/include/zdev_id.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* zdev - Modify and display the persistent configuration of devices
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef ZDEV_ID_H
|
||||
#define ZDEV_ID_H
|
||||
|
||||
#define ZDEV_SITE_ID_FILE "/run/zdev_id.env"
|
||||
|
||||
#endif /* ZDEV_ID_H */
|
||||
@@ -82,6 +82,42 @@ when a device driver is loaded.
|
||||
.PP
|
||||
By default chzdev applies changes to both the active and the persistent
|
||||
configuration.
|
||||
.
|
||||
.SS "Site-specific configurations"
|
||||
In some high-availability setups, root and boot disks of a Linux installation are
|
||||
copied to remote sites that can take over execution of a Linux workload in case of
|
||||
an outage of the original site. A
|
||||
.B site
|
||||
in this context is an execution environment such as an
|
||||
.B LPAR or
|
||||
.B z/VM guest.
|
||||
Each site may provide a different set of devices, or require different parameters to be
|
||||
applied per device.
|
||||
|
||||
chzdev supports site-specific device configuration for up to
|
||||
.B 10
|
||||
sites. Each site is identified by a number in the range 0 to 9. You can provide a separate
|
||||
set of persistent device configuration for each site. The IPL Load Parameter value specified
|
||||
during boot determines the currently active site. Only the device configuration of the active
|
||||
site is applied during boot and when new devices become available. A common configuration can
|
||||
be provided that is applied when no site-specific configuration is available for a device in
|
||||
the active site.
|
||||
|
||||
You can use the
|
||||
.B --site
|
||||
parameter to configure devices for a specific site only. Configuration actions without a site
|
||||
parameter apply to the common configuration.
|
||||
|
||||
Linux supports the following IPL Load Parameter values for selecting the active site:
|
||||
|
||||
.B S<n>
|
||||
Site with number <n> becomes the active site. Example: "S2" specifies site 2 as active site.
|
||||
|
||||
.B SS
|
||||
The Subchannel-Set ID (SSID) of the IPL device specifies the number of the active site.
|
||||
|
||||
If no site is specified in the Load Parameter, only the common configuration is applied.
|
||||
|
||||
.PP
|
||||
.
|
||||
.SS "Tool actions"
|
||||
@@ -392,6 +428,10 @@ Removes configuration files and settings associated with the
|
||||
selected device.
|
||||
.PP
|
||||
.
|
||||
.OD disable-all "D" ""
|
||||
Disable device in all configurations.
|
||||
.PP
|
||||
.
|
||||
.OD enable "e" ""
|
||||
Enable device.
|
||||
|
||||
@@ -583,6 +623,34 @@ Apply changes to persistent configuration only.
|
||||
|
||||
The persistent configuration is put into effect when the system boots, or
|
||||
when a device becomes available, or when a device driver is loaded.
|
||||
.PP
|
||||
.
|
||||
.OD site "s" "SITE-ID"
|
||||
Apply the persistent configuration to the specified site only.
|
||||
|
||||
This option can be used to configure the device in such a way that, the
|
||||
same configuration can be used on multiple different sites with different
|
||||
device hardwares, without any modification. With the site information, we
|
||||
can add different configurations which are specific to different sites. Where
|
||||
each site represents different systems or different hardware devices which
|
||||
are even located in different locations.
|
||||
For example, having
|
||||
the configuration of main-site or failover-site in the same place, but these
|
||||
configurations used only on the respective sites. Currently a user can define
|
||||
10 different configurations for 10 different sites with
|
||||
.I SITE-ID
|
||||
0 to 9.
|
||||
|
||||
The configuration settings is put into effect only on the system with the
|
||||
site number which is mentioned in
|
||||
.I SITE-ID.
|
||||
|
||||
This parameter is only effective only for persistent configurations and for
|
||||
dasd device-types.
|
||||
|
||||
.B Example:
|
||||
.CL chzdev -ep 0.0.f001 --site 3
|
||||
|
||||
.PP
|
||||
.
|
||||
.OD quiet "q" ""
|
||||
|
||||
@@ -74,9 +74,43 @@ of the currently running system, the persistent configuration stored in
|
||||
configuration files, and the auto-configuration that is provided by some
|
||||
machine types to automatically enable I/O devices.
|
||||
.PP
|
||||
Persistent configuration data is of two different types. Site-specific configuration
|
||||
and common-configuration. Common configuration refers to the persistent configuration
|
||||
which is not bound to any site. In many use cases, this configuration is being
|
||||
used as a fallback configuration.
|
||||
Refer the
|
||||
.B site
|
||||
section below to learn more about site-specific configuration.
|
||||
.PP
|
||||
By default lszdev displays information from both the active and the persistent
|
||||
configuration, and, if available, from the auto-configuration.
|
||||
.PP
|
||||
.SS Output Format
|
||||
In list format, lszdev uses values with the following meaning for the persistent colums (PERS):
|
||||
|
||||
.B yes
|
||||
.CL A persistent, non-site specific configuration exists; No site specific configurations available.
|
||||
|
||||
.B no
|
||||
.CL No persistent configurations is available.
|
||||
|
||||
.B auto
|
||||
.CL Machine-provided auto-configuration is available.
|
||||
|
||||
.B s<n>
|
||||
.CL A site-specific configuration is available for site-id n.
|
||||
|
||||
.B s+
|
||||
.CL Site specific configurations are available for multiple sites.
|
||||
|
||||
.B Example
|
||||
|
||||
.CL TYPE ID ON PERS NAMES
|
||||
.CL dasd-eckd 0.0.f001 no s+ dasda
|
||||
.CL dasd-eckd 0.0.f002 no s4 dasdb
|
||||
.CL dasd-eckd 0.0.f003 no no
|
||||
.CL qeth 0.0.1200:0.0.1201:0.0.1202 no yes encbdf0
|
||||
|
||||
.
|
||||
.
|
||||
.SH TYPE
|
||||
@@ -123,6 +157,7 @@ comma (,).
|
||||
.PP
|
||||
.
|
||||
.
|
||||
|
||||
.SH SELECTION
|
||||
In addition to specifying devices by their ID, you can also select devices by
|
||||
their state or by the Linux functions they provide.
|
||||
@@ -292,6 +327,7 @@ Display detailed information.
|
||||
Displays detailed information about the configuration of the selected device
|
||||
or device type. Specifying \-\-info twice will display additional device
|
||||
information.
|
||||
|
||||
.PP
|
||||
.
|
||||
.OD list-columns "l" ""
|
||||
@@ -392,6 +428,25 @@ escaped by placing a slash in front of it.
|
||||
List information from the persistent configuration only.
|
||||
|
||||
Restricts output to information obtained from configuration files.
|
||||
.PP
|
||||
.
|
||||
.OD site "s" "SITE-ID"
|
||||
List the persistent configuration from the specified site only.
|
||||
|
||||
The resulted persistent configuration details are filtered based on the
|
||||
.ID SITE-ID
|
||||
value. The site specific information can be found in a basic lszdev output.
|
||||
For example, the PERS column may show "s+" which indicates that the device
|
||||
has multiple configurations for multiple sites, and "sn" where 'n' is an
|
||||
number between 0 to 9, which indicates that the persistent configuration
|
||||
for the device is available only for site 'n'.
|
||||
|
||||
.B Example:
|
||||
|
||||
.CL lszdev --site 3
|
||||
.CL lszdev -i --site 2
|
||||
.CL lszdev f001 --site 9
|
||||
|
||||
.PP
|
||||
.
|
||||
.OD quiet "q" ""
|
||||
|
||||
@@ -81,9 +81,6 @@ lszdev_objects += generic_ccw.o
|
||||
|
||||
all: chzdev lszdev zdev_id
|
||||
|
||||
ifneq (${HAVE_LOCKFILE}, 0)
|
||||
LDLIBS += -llockfile
|
||||
endif
|
||||
ifneq (${HAVE_JSONC}, 0)
|
||||
LDLIBS += -ljson-c
|
||||
endif
|
||||
|
||||
@@ -1593,7 +1593,7 @@ static exit_code_t ccw_st_deconfigure_active(struct subtype *st,
|
||||
static exit_code_t ccw_st_deconfigure_persistent(struct subtype *st,
|
||||
struct device *dev)
|
||||
{
|
||||
return udev_remove_rule(st->name, dev->id, false);
|
||||
return ccw_st_configure_persistent(st, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "lib/util_path.h"
|
||||
#include "lib/zt_common.h"
|
||||
@@ -44,6 +45,12 @@
|
||||
#include "udev.h"
|
||||
#include "zfcp_lun.h"
|
||||
|
||||
/* current site-id in action. By default, operations are always on fallback
|
||||
* site; We need the current site-id as global variable to avoid excessive
|
||||
* diff of simple function parameter modifications to pass this value.
|
||||
*/
|
||||
int global_site_id = SITE_FALLBACK;
|
||||
|
||||
/* Main program action. */
|
||||
typedef enum {
|
||||
ACT_CONFIGURE,
|
||||
@@ -73,6 +80,7 @@ struct options {
|
||||
/* Actions */
|
||||
unsigned int enable:1;
|
||||
unsigned int deconfigure:1;
|
||||
unsigned int deconfigure_all:1;
|
||||
unsigned int list_attribs:1;
|
||||
unsigned int list_types:1;
|
||||
unsigned int help_attribs:1;
|
||||
@@ -97,6 +105,7 @@ struct options {
|
||||
unsigned int verbose:1;
|
||||
unsigned int quiet:1;
|
||||
unsigned int no_settle:1;
|
||||
unsigned int site_id;
|
||||
};
|
||||
|
||||
/* Makefile converts chzdev_usage.txt into C file which we include here. */
|
||||
@@ -119,6 +128,7 @@ enum {
|
||||
OPT_TYPE = 't',
|
||||
OPT_ENABLE = 'e',
|
||||
OPT_DECONFIGURE = 'd',
|
||||
OPT_DECONFIGURE_ALL = 'D',
|
||||
OPT_LIST_ATTRIBS = 'l',
|
||||
OPT_HELP_ATTRIBS = 'H',
|
||||
OPT_LIST_TYPES = 'L',
|
||||
@@ -140,43 +150,52 @@ enum {
|
||||
OPT_QUIET = 'q',
|
||||
OPT_NO_SETTLE = (OPT_ANONYMOUS_BASE+__COUNTER__),
|
||||
OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__),
|
||||
OPT_SITE = 's',
|
||||
};
|
||||
|
||||
static struct opts_conflict conflict_list[] = {
|
||||
OPTS_CONFLICT(OPT_ENABLE,
|
||||
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_LIST_TYPES,
|
||||
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_TYPE, 0),
|
||||
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_TYPE,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_DECONFIGURE,
|
||||
OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS, OPT_LIST_TYPES,
|
||||
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, OPT_TYPE, 0),
|
||||
OPT_REMOVE_ALL, OPT_TYPE,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_LIST_ATTRIBS,
|
||||
OPT_DECONFIGURE, OPT_HELP_ATTRIBS, OPT_LIST_TYPES,
|
||||
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
|
||||
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
|
||||
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED, 0),
|
||||
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_LIST_TYPES,
|
||||
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
|
||||
OPT_EXPORT, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE,
|
||||
OPT_OFFLINE, OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE,
|
||||
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED, 0),
|
||||
OPT_BY_ATTRIB, OPT_ACTIVE, OPT_PERSISTENT, OPT_FAILED,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_EXPORT,
|
||||
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
|
||||
OPT_LIST_TYPES, OPT_IMPORT, OPT_APPLY, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, 0),
|
||||
OPT_REMOVE_ALL, OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_IMPORT,
|
||||
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
|
||||
OPT_LIST_TYPES, OPT_EXPORT, OPT_APPLY, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, OPT_CONFIGURED, OPT_EXISTING, OPT_BY_PATH,
|
||||
OPT_BY_ATTRIB, OPT_BY_NODE, OPT_BY_INTERFACE, 0),
|
||||
OPT_BY_ATTRIB, OPT_BY_NODE, OPT_BY_INTERFACE,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_APPLY,
|
||||
OPT_DECONFIGURE, OPT_LIST_ATTRIBS, OPT_HELP_ATTRIBS,
|
||||
OPT_LIST_TYPES, OPT_EXPORT, OPT_IMPORT, OPT_REMOVE,
|
||||
OPT_REMOVE_ALL, OPT_ACTIVE, 0),
|
||||
OPT_REMOVE_ALL, OPT_ACTIVE,
|
||||
OPT_DECONFIGURE_ALL, 0),
|
||||
OPTS_CONFLICT(OPT_ONLINE,
|
||||
OPT_OFFLINE),
|
||||
OPTS_CONFLICT(OPT_SITE,
|
||||
OPT_TYPE, 0),
|
||||
OPTS_CONFLICT(OPT_QUIET,
|
||||
OPT_VERBOSE),
|
||||
OPTS_CONFLICT(OPT_AUTO_CONF,
|
||||
@@ -202,6 +221,7 @@ static const struct option opt_list[] = {
|
||||
/* Actions. */
|
||||
{ "enable", no_argument, NULL, OPT_ENABLE },
|
||||
{ "disable", no_argument, NULL, OPT_DECONFIGURE },
|
||||
{ "disable-all", no_argument, NULL, OPT_DECONFIGURE_ALL },
|
||||
{ "list-attributes", no_argument, NULL, OPT_LIST_ATTRIBS },
|
||||
{ "help-attribute", no_argument, NULL, OPT_HELP_ATTRIBS },
|
||||
{ "list-types", no_argument, NULL, OPT_LIST_TYPES },
|
||||
@@ -225,11 +245,12 @@ static const struct option opt_list[] = {
|
||||
{ "verbose", no_argument, NULL, OPT_VERBOSE },
|
||||
{ "quiet", no_argument, NULL, OPT_QUIET },
|
||||
{ "no-settle", no_argument, NULL, OPT_NO_SETTLE },
|
||||
{ "site", required_argument, NULL, OPT_SITE },
|
||||
{ NULL, no_argument, NULL, 0 },
|
||||
};
|
||||
|
||||
/* Command line abbreviations. */
|
||||
static const char opt_str[] = ":edlHLapr:RfyhvVqt";
|
||||
static const char opt_str[] = ":edlHLapr:RfyhvVqts:D";
|
||||
|
||||
/* Count of persistently modified devices. */
|
||||
static int pers_mod_devs;
|
||||
@@ -246,6 +267,8 @@ static void init_options(struct options *opts)
|
||||
opts->settings = strlist_new();
|
||||
opts->remove = strlist_new();
|
||||
opts->base = strlist_new();
|
||||
/* Default operations are on fallback site*/
|
||||
opts->site_id = SITE_FALLBACK;
|
||||
}
|
||||
|
||||
/* Release memory used in options data structure. */
|
||||
@@ -838,6 +861,12 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
opts->deconfigure = 1;
|
||||
break;
|
||||
|
||||
case OPT_DECONFIGURE_ALL:
|
||||
/* --disable all the settings*/
|
||||
opts->deconfigure = 1;
|
||||
opts->deconfigure_all = 1;
|
||||
break;
|
||||
|
||||
case OPT_LIST_ATTRIBS:
|
||||
/* --list-attributes */
|
||||
opts->list_attribs = 1;
|
||||
@@ -955,6 +984,28 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
opts->no_settle = 1;
|
||||
break;
|
||||
|
||||
case OPT_SITE:
|
||||
/* --site */
|
||||
/* 1. User can specify only site-ids from 0 to 9
|
||||
* 2. only one --site parameter is accepted
|
||||
*/
|
||||
|
||||
if (!is_valid_site(optarg)) {
|
||||
syntax("Unsupported site ID\n");
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
|
||||
if (opts->site_id != SITE_FALLBACK) {
|
||||
syntax("Cannot specify '--site' multiple "
|
||||
"times\n");
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
|
||||
opts->site_id = atoi(optarg);
|
||||
global_site_id = opts->site_id;
|
||||
opts->persistent = 1;
|
||||
break;
|
||||
|
||||
case ':':
|
||||
/* Missing option argument. */
|
||||
syntax("Option '%s' requires an argument\n",
|
||||
@@ -987,7 +1038,9 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
goto out;
|
||||
|
||||
/* Determine configuration set. */
|
||||
if (!opts->active && !opts->persistent && !opts->auto_conf) {
|
||||
if (opts->deconfigure_all) {
|
||||
opts->config = config_all;
|
||||
} else if (!opts->active && !opts->persistent && !opts->auto_conf) {
|
||||
/* Default configuration targets are active + persistent */
|
||||
opts->config = config_active | config_persistent;
|
||||
} else {
|
||||
@@ -1794,6 +1847,24 @@ static void unblacklist_ranges(struct selected_dev_node *sel,
|
||||
*param_ptr = param;
|
||||
}
|
||||
|
||||
/* Currently the site-specific configurations are supported only
|
||||
* for some device types. Make sure that user get an error message when
|
||||
* try to configure other device-types with site parameter.
|
||||
*/
|
||||
static exit_code_t is_site_supported(struct util_list *selected)
|
||||
{
|
||||
struct selected_dev_node *sel;
|
||||
|
||||
util_list_iterate(selected, sel) {
|
||||
if (sel->dt && !sel->dt->site_support) {
|
||||
error("Site specific configuration is not supported"
|
||||
" for %s devices\n", sel->dt->devname);
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
}
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
/* Handle device configuration. */
|
||||
static exit_code_t configure_devices(struct options *opts, int specified,
|
||||
int *found_ptr)
|
||||
@@ -1827,6 +1898,9 @@ static exit_code_t configure_devices(struct options *opts, int specified,
|
||||
error("No device was selected!\n");
|
||||
rc = EXIT_EMPTY_SELECTION;
|
||||
goto out;
|
||||
} else if (opts->site_id != SITE_FALLBACK) {
|
||||
if (is_site_supported(selected))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Work on selected devices. */
|
||||
@@ -1913,6 +1987,18 @@ static exit_code_t check_in_use(struct device *dev)
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
static bool is_site_modified(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_USER_SITES; i++) {
|
||||
if (dev->site_specific[i].modified)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Deconfigure specified device. */
|
||||
static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
|
||||
struct options *opts, int try,
|
||||
@@ -1922,6 +2008,7 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
|
||||
exit_code_t rc;
|
||||
struct device *dev;
|
||||
config_t config, read_config;
|
||||
int i, site_available = 0;
|
||||
|
||||
/* Reset processed flag. */
|
||||
if (proc_ptr)
|
||||
@@ -1954,7 +2041,7 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
|
||||
dev->processed = 1;
|
||||
|
||||
if (try && !(dev->active.exists || dev->active.definable ||
|
||||
dev->persistent.exists || dev->autoconf.exists)) {
|
||||
is_dev_pers(dev) || dev->autoconf.exists)) {
|
||||
/* Attempt to deconfigure this device will fail. */
|
||||
return EXIT_DEVICE_NOT_FOUND;
|
||||
}
|
||||
@@ -1964,7 +2051,7 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
|
||||
* dev. */
|
||||
if (SCOPE_ACTIVE(config) &&
|
||||
!(dev->active.exists || dev->active.definable)) {
|
||||
if (!((SCOPE_PERSISTENT(config) && dev->persistent.exists) ||
|
||||
if (!((SCOPE_PERSISTENT(config) && is_dev_pers(dev)) ||
|
||||
(SCOPE_AUTOCONF(config) && dev->autoconf.exists)))
|
||||
return EXIT_DEVICE_NOT_FOUND;
|
||||
}
|
||||
@@ -1985,16 +2072,48 @@ static exit_code_t deconfigure_one_device(struct subtype *st, const char *id,
|
||||
} else if (subtype_online_get(st, dev, config_active) == 1)
|
||||
dev->active.modified = 1;
|
||||
}
|
||||
if (SCOPE_PERSISTENT(config) && dev->persistent.exists) {
|
||||
dev->persistent.deconfigured = 1;
|
||||
dev->persistent.modified = 1;
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
if (dev->persistent.exists) {
|
||||
dev->persistent.deconfigured = 1;
|
||||
dev->persistent.modified = 1;
|
||||
} else if (is_dev_pers(dev) && global_site_id != SITE_FALLBACK) {
|
||||
dev->persistent.deconfigured = 1;
|
||||
dev->persistent.modified = 1;
|
||||
}
|
||||
}
|
||||
if (SCOPE_AUTOCONF(config) && dev->autoconf.exists) {
|
||||
dev->autoconf.deconfigured = 1;
|
||||
dev->autoconf.modified = 1;
|
||||
}
|
||||
|
||||
/* In case of deconfigure_all command, make sure that site specific
|
||||
* settings are also removed.
|
||||
*/
|
||||
if (opts->deconfigure_all) {
|
||||
for (i = 0; i < NUM_USER_SITES; i++) {
|
||||
if (dev->site_specific[i].exists) {
|
||||
site_available++;
|
||||
/* To remove site-specific settings, simply
|
||||
* make the deconfigured = 1
|
||||
*/
|
||||
dev->site_specific[i].deconfigured = 1;
|
||||
dev->site_specific[i].modified = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (site_available) {
|
||||
/* set persistent.exists = 1 to let the
|
||||
* drivers know that we need to modify the
|
||||
* udev-rule
|
||||
*/
|
||||
dev->persistent.exists = 1;
|
||||
dev->persistent.modified = 1;
|
||||
dev->persistent.deconfigured = 1;
|
||||
}
|
||||
|
||||
if (!dev->active.modified && !dev->persistent.modified &&
|
||||
!dev->autoconf.modified)
|
||||
!dev->autoconf.modified && !is_site_modified(dev))
|
||||
return EXIT_OK;
|
||||
|
||||
/* Pre-write check. */
|
||||
@@ -2036,6 +2155,9 @@ static exit_code_t deconfigure_devices(struct options *opts)
|
||||
error("No device was selected!\n");
|
||||
rc = EXIT_EMPTY_SELECTION;
|
||||
goto out;
|
||||
} else if (opts->site_id != SITE_FALLBACK) {
|
||||
if (is_site_supported(selected))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Work on selected devices. */
|
||||
@@ -2558,8 +2680,13 @@ static void action_note(const char *msg, config_t config)
|
||||
{
|
||||
if (config == config_active)
|
||||
info("%s the active configuration only\n", msg);
|
||||
else if (config == config_persistent)
|
||||
info("%s the persistent configuration only\n", msg);
|
||||
else if (config == config_persistent) {
|
||||
if (global_site_id == SITE_FALLBACK)
|
||||
info("%s the persistent configuration only\n", msg);
|
||||
else
|
||||
info("%s the site %d configuration only\n", msg,
|
||||
global_site_id);
|
||||
}
|
||||
else if (config == config_autoconf)
|
||||
info("%s the auto-configuration only\n", msg);
|
||||
}
|
||||
@@ -3034,7 +3161,7 @@ int main(int argc, char *argv[])
|
||||
!dryrun) {
|
||||
/* If the root device/device type or early devices have been
|
||||
* modified, additional work might be necessary. */
|
||||
rc = initrd_check(ZDEV_ALWAYS_UPDATE_INITRD);
|
||||
rc = initrd_check(ZDEV_ALWAYS_UPDATE_INITRD, opts.deconfigure_all);
|
||||
if (rc && !drc)
|
||||
drc = rc;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ SETTINGS
|
||||
|
||||
ACTIONS
|
||||
-e, --enable Enable device
|
||||
-d, --disable Disable device
|
||||
-d, --disable Disable specified setting of the device
|
||||
-D, --disable-all Disable all the settings of the device
|
||||
-l, --list-attributes List attributes
|
||||
-L, --list-types List supported device types
|
||||
-H, --help-attribute Show detailed help on specified attribute
|
||||
@@ -56,5 +57,6 @@ OPTIONS
|
||||
--base PATH Use PATH as base for accessing files
|
||||
--no-settle Do not wait for udev to settle
|
||||
--auto-conf Apply changes to auto-configuration only
|
||||
-s, --site ID Apply changes to the specified site only
|
||||
-V, --verbose Print additional run-time information
|
||||
-q, --quiet Print only minimal run-time information
|
||||
|
||||
@@ -688,6 +688,7 @@ struct devtype dasd_devtype = {
|
||||
"(DASDs)",
|
||||
.devname = "DASD",
|
||||
.modules = STRING_ARRAY(DASD_MOD_NAME),
|
||||
.site_support = 1,
|
||||
|
||||
.subtypes = SUBTYPE_ARRAY(
|
||||
&dasd_subtype_eckd,
|
||||
|
||||
@@ -29,6 +29,7 @@ struct device *device_new(struct subtype *st, const char *id)
|
||||
{
|
||||
struct device *dev;
|
||||
struct namespace *ns = st->namespace;
|
||||
int i;
|
||||
|
||||
dev = misc_malloc(sizeof(struct device));
|
||||
dev->subtype = st;
|
||||
@@ -44,12 +45,17 @@ struct device *device_new(struct subtype *st, const char *id)
|
||||
dev->persistent.settings = setting_list_new();
|
||||
dev->autoconf.settings = setting_list_new();
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++)
|
||||
dev->site_specific[i].settings = setting_list_new();
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* Release all resources associated with the specified device. */
|
||||
void device_free(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
free(dev->id);
|
||||
@@ -57,12 +63,18 @@ void device_free(struct device *dev)
|
||||
setting_list_free(dev->active.settings);
|
||||
setting_list_free(dev->persistent.settings);
|
||||
setting_list_free(dev->autoconf.settings);
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++)
|
||||
setting_list_free(dev->site_specific[i].settings);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
/* Used for debugging. */
|
||||
void device_print(struct device *dev, int level)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("%*sdevice at %p:\n", level, "", (void *) dev);
|
||||
if (!dev)
|
||||
return;
|
||||
@@ -96,6 +108,36 @@ void device_print(struct device *dev, int level)
|
||||
setting_list_print(dev->autoconf.settings, level + 8);
|
||||
else
|
||||
printf("%*s<none>\n", level + 8, "");
|
||||
|
||||
printf("%*spersistent-site-specific:\n", level + 4, "");
|
||||
for (i = 0; i < NUM_SITES; i++) {
|
||||
printf("%*sexists=%d mod=%d deconf=%d\n",
|
||||
level + 8, "", dev->site_specific[i].exists,
|
||||
dev->site_specific[i].modified,
|
||||
dev->site_specific[i].deconfigured);
|
||||
if (dev->site_specific[i].settings)
|
||||
setting_list_print(dev->site_specific[i].settings, level + 8);
|
||||
else
|
||||
printf("%*s<none>\n", level + 8, "");
|
||||
}
|
||||
}
|
||||
|
||||
char *device_get_sites(struct device *dev)
|
||||
{
|
||||
struct util_list *names;
|
||||
char *str;
|
||||
int i;
|
||||
|
||||
names = strlist_new();
|
||||
for (i = 0; i < NUM_USER_SITES; i++) {
|
||||
if (dev->site_specific[i].exists)
|
||||
strlist_add(names, "%d", i);
|
||||
}
|
||||
|
||||
str = strlist_flatten(names, ",");
|
||||
strlist_free(names);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static const void *device_hash_get_id(void *dev_ptr)
|
||||
@@ -627,10 +669,16 @@ exit_code_t device_check_settings(struct device *dev, config_t config,
|
||||
}
|
||||
|
||||
struct setting_list *device_get_setting_list(struct device *dev,
|
||||
config_t config)
|
||||
config_t config,
|
||||
int site_id)
|
||||
{
|
||||
struct setting_list *settings = NULL;
|
||||
|
||||
if (site_id < SITE_FALLBACK) {
|
||||
settings = dev->site_specific[site_id].settings;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (config == config_active)
|
||||
settings = dev->active.settings;
|
||||
else if (config == config_persistent)
|
||||
@@ -638,6 +686,7 @@ struct setting_list *device_get_setting_list(struct device *dev,
|
||||
else if (config == config_autoconf)
|
||||
settings = dev->autoconf.settings;
|
||||
|
||||
out:
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -657,3 +706,21 @@ config_t device_get_config(struct device *dev)
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/* Determine if persistent configuration available for any of the sites*/
|
||||
bool is_dev_pers(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (global_site_id != SITE_FALLBACK &&
|
||||
!dev->site_specific[global_site_id].exists)
|
||||
return false;
|
||||
else if (dev->persistent.exists)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++)
|
||||
if (dev->site_specific[i].exists)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "setting.h"
|
||||
#include "subtype.h"
|
||||
|
||||
static int import_site_id = SITE_FALLBACK;
|
||||
|
||||
struct export_header {
|
||||
export_t type;
|
||||
config_t config;
|
||||
@@ -28,13 +30,28 @@ struct export_header {
|
||||
char *id;
|
||||
};
|
||||
|
||||
static bool header_str_to_config(const char *config_str, config_t *config)
|
||||
{
|
||||
int site;
|
||||
|
||||
if (strstr(config_str, "site")) {
|
||||
if (sscanf(config_str, "site%d", &site) == 1) {
|
||||
import_site_id = site;
|
||||
*config = config_persistent;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
import_site_id = SITE_FALLBACK;
|
||||
return str_to_config(config_str, config);
|
||||
}
|
||||
|
||||
static struct export_header *header_new(const char *config_str,
|
||||
const char *type, const char *id)
|
||||
{
|
||||
config_t config;
|
||||
struct export_header *hdr;
|
||||
|
||||
if (!str_to_config(config_str, &config))
|
||||
if (!header_str_to_config(config_str, &config))
|
||||
return NULL;
|
||||
hdr = misc_malloc(sizeof(struct export_header));
|
||||
if (!id)
|
||||
@@ -130,6 +147,14 @@ static void export_write_comment(FILE *fd)
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
}
|
||||
|
||||
static const char *site_specific_header(config_t config)
|
||||
{
|
||||
if (global_site_id != SITE_FALLBACK)
|
||||
return misc_asprintf("site%d", global_site_id);
|
||||
else
|
||||
return config_to_str(config);
|
||||
}
|
||||
|
||||
/* Write an export file header. */
|
||||
static void write_header(FILE *fd, config_t config, const char *type,
|
||||
const char *id, int *first_ptr)
|
||||
@@ -143,9 +168,9 @@ static void write_header(FILE *fd, config_t config, const char *type,
|
||||
}
|
||||
|
||||
if (id)
|
||||
fprintf(fd, "[%s %s %s]\n", config_to_str(config), type, id);
|
||||
fprintf(fd, "[%s %s %s]\n", site_specific_header(config), type, id);
|
||||
else
|
||||
fprintf(fd, "[%s %s]\n", config_to_str(config), type);
|
||||
fprintf(fd, "[%s %s]\n", site_specific_header(config), type);
|
||||
}
|
||||
|
||||
static int count_exportable(struct device *dev, config_t config)
|
||||
@@ -154,7 +179,7 @@ static int count_exportable(struct device *dev, config_t config)
|
||||
struct setting *s;
|
||||
int count;
|
||||
|
||||
list = device_get_setting_list(dev, config);
|
||||
list = device_get_setting_list(dev, config, global_site_id);
|
||||
if (!list)
|
||||
return 0;
|
||||
count = 0;
|
||||
@@ -408,7 +433,11 @@ static exit_code_t handle_header(const char *filename, int lineno,
|
||||
dev->active.exists = 1;
|
||||
}
|
||||
if (SCOPE_PERSISTENT(hdr->config)) {
|
||||
setting_list_clear(dev->persistent.settings);
|
||||
if (import_site_id != SITE_FALLBACK)
|
||||
setting_list_clear(dev->site_specific[import_site_id].settings);
|
||||
else
|
||||
setting_list_clear(dev->persistent.settings);
|
||||
|
||||
dev->persistent.exists = 1;
|
||||
}
|
||||
if (SCOPE_AUTOCONF(hdr->config)) {
|
||||
@@ -462,7 +491,7 @@ static exit_code_t handle_setting(const char *filename, int lineno,
|
||||
} else if (dev) {
|
||||
/* We're inside a device section. */
|
||||
attribs = dev->subtype->dev_attribs;
|
||||
list = device_get_setting_list(dev, config);
|
||||
list = device_get_setting_list(dev, config, SITE_FALLBACK);
|
||||
} else
|
||||
return EXIT_OK;
|
||||
|
||||
@@ -502,7 +531,8 @@ static bool empty_device(struct device *dev)
|
||||
return false;
|
||||
if (util_list_is_empty(&dev->active.settings->list) &&
|
||||
util_list_is_empty(&dev->persistent.settings->list) &&
|
||||
util_list_is_empty(&dev->autoconf.settings->list))
|
||||
util_list_is_empty(&dev->autoconf.settings->list) &&
|
||||
import_site_id == SITE_FALLBACK)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -574,8 +604,9 @@ exit_code_t export_read(FILE *fd, const char *filename,
|
||||
ptrlist_add(objects, object_new(export_devtype,
|
||||
dt));
|
||||
} else if (dev) {
|
||||
ptrlist_add(objects, object_new(export_device,
|
||||
dev));
|
||||
if (global_site_id == import_site_id)
|
||||
ptrlist_add(objects, object_new(export_device,
|
||||
dev));
|
||||
}
|
||||
} else if (parse_setting(l, &key, &value)) {
|
||||
/* Found <key>=<value>. */
|
||||
@@ -584,8 +615,10 @@ exit_code_t export_read(FILE *fd, const char *filename,
|
||||
"valid section\n");
|
||||
rc = EXIT_FORMAT_ERROR;
|
||||
} else {
|
||||
rc = handle_setting(filename, lineno, key,
|
||||
value, dt, dev, config);
|
||||
if (global_site_id == import_site_id ||
|
||||
import_site_id == SITE_FALLBACK)
|
||||
rc = handle_setting(filename, lineno, key,
|
||||
value, dt, dev, config);
|
||||
}
|
||||
free(key);
|
||||
free(value);
|
||||
|
||||
@@ -348,7 +348,7 @@ static void _add_setting(const char *filename, struct device *dev,
|
||||
struct attrib *a;
|
||||
struct setting_list *list;
|
||||
|
||||
list = device_get_setting_list(dev, config);
|
||||
list = device_get_setting_list(dev, config, SITE_FALLBACK);
|
||||
a = attrib_find(dev->subtype->dev_attribs, key);
|
||||
if (!a) {
|
||||
warnx("%s: Applying unknown device setting %s=%s", filename,
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
#include "table.h"
|
||||
#include "table_types.h"
|
||||
|
||||
/* current site-id in action. By default, operations are always on fallback
|
||||
* site; We need the current site-id as global variable to avoid excessive
|
||||
* diff of simple function parameter modifications to pass this value.
|
||||
*/
|
||||
int global_site_id = SITE_FALLBACK;
|
||||
|
||||
/* Main program action. */
|
||||
typedef enum {
|
||||
ACT_LIST,
|
||||
@@ -66,6 +72,7 @@ struct options {
|
||||
unsigned int pairs:1;
|
||||
unsigned int verbose:1;
|
||||
unsigned int quiet:1;
|
||||
unsigned int site_id;
|
||||
};
|
||||
|
||||
/* Makefile converts lszdev_usage.txt into C file which we include here. */
|
||||
@@ -100,6 +107,7 @@ enum {
|
||||
OPT_QUIET = 'q',
|
||||
OPT_PAIRS = 'P',
|
||||
OPT_AUTO_CONF = (OPT_ANONYMOUS_BASE+__COUNTER__),
|
||||
OPT_SITE = 's',
|
||||
};
|
||||
|
||||
static struct opts_conflict conflict_list[] = {
|
||||
@@ -120,6 +128,8 @@ static struct opts_conflict conflict_list[] = {
|
||||
OPT_CONFIGURED, OPT_EXISTING, OPT_ONLINE, OPT_OFFLINE,
|
||||
OPT_BY_PATH, OPT_BY_NODE, OPT_BY_INTERFACE, OPT_FAILED,
|
||||
0),
|
||||
OPTS_CONFLICT(OPT_SITE,
|
||||
OPT_TYPE),
|
||||
OPTS_CONFLICT(OPT_ONLINE,
|
||||
OPT_OFFLINE),
|
||||
OPTS_CONFLICT(OPT_QUIET,
|
||||
@@ -159,11 +169,12 @@ static const struct option opt_list[] = {
|
||||
{ "pairs", no_argument, NULL, OPT_PAIRS },
|
||||
{ "verbose", no_argument, NULL, OPT_VERBOSE },
|
||||
{ "quiet", no_argument, NULL, OPT_QUIET },
|
||||
{ "site", required_argument, NULL, OPT_SITE },
|
||||
{ NULL, no_argument, NULL, 0 },
|
||||
};
|
||||
|
||||
/* Command line abbreviations. */
|
||||
static const char opt_str[] = ":tilLhvapPc:nVq";
|
||||
static const char opt_str[] = ":tilLhvapPc:nVqs:";
|
||||
|
||||
/* Initialize options data structure. */
|
||||
static void init_options(struct options *opts)
|
||||
@@ -172,6 +183,8 @@ static void init_options(struct options *opts)
|
||||
opts->select = select_opts_new();
|
||||
opts->columns = strlist_new();
|
||||
opts->base = strlist_new();
|
||||
/* Default operations are on fallback site*/
|
||||
opts->site_id = SITE_FALLBACK;
|
||||
}
|
||||
|
||||
/* Release memory used in options data structure. */
|
||||
@@ -212,6 +225,41 @@ static action_t get_action(struct options *opts)
|
||||
return ACT_LIST;
|
||||
}
|
||||
|
||||
/* Determine the site information from the device */
|
||||
static char *get_site_from_pers(struct device *dev)
|
||||
{
|
||||
int site, i, num = 0;
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++) {
|
||||
if (dev->site_specific[i].exists) {
|
||||
site = i;
|
||||
num++;
|
||||
if (site == global_site_id && site != SITE_FALLBACK)
|
||||
return misc_asprintf("s%d", site);
|
||||
}
|
||||
}
|
||||
|
||||
/* Incase of devices which do not have --site support, all the
|
||||
* read configurations will be in the dev->persistent and not
|
||||
* in dev->site_specific.
|
||||
*/
|
||||
if (num == 0 && dev->persistent.exists == 1 &&
|
||||
global_site_id == SITE_FALLBACK)
|
||||
return misc_asprintf("yes");
|
||||
|
||||
/* Found more site-specific information ? return s+ */
|
||||
if (num > 1)
|
||||
return misc_asprintf("s+");
|
||||
|
||||
if (num == 1)
|
||||
if (site == SITE_FALLBACK)
|
||||
return misc_asprintf("yes");
|
||||
else
|
||||
return misc_asprintf("s%d", site);
|
||||
else
|
||||
return misc_asprintf("no");
|
||||
}
|
||||
|
||||
/* Check options data structure for syntax errors. */
|
||||
static exit_code_t check_options(struct options *opts,
|
||||
int specified[OPTS_MAX + 1], int op)
|
||||
@@ -661,6 +709,27 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
opts->quiet = 1;
|
||||
break;
|
||||
|
||||
case OPT_SITE:
|
||||
/* --site */
|
||||
/* User can specify only site-ids from 0 to 9 */
|
||||
if (!is_valid_site(optarg)) {
|
||||
syntax("Unsupported site ID\n");
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
|
||||
if (opts->site_id != SITE_FALLBACK) {
|
||||
syntax("Cannot specify '--site' multiple "
|
||||
"times\n");
|
||||
return EXIT_USAGE_ERROR;
|
||||
}
|
||||
|
||||
/* site information is a persistent configuration.
|
||||
* set opts->persistent here.
|
||||
*/
|
||||
opts->persistent = 1;
|
||||
opts->site_id = atoi(optarg);
|
||||
break;
|
||||
|
||||
case ':':
|
||||
/* Missing option argument. */
|
||||
syntax("Option '%s' requires an argument\n",
|
||||
@@ -718,7 +787,8 @@ static exit_code_t parse_options(struct options *opts, int argc, char *argv[])
|
||||
opts->select->all = 1;
|
||||
else if (opts->config == config_active)
|
||||
opts->select->existing = 1;
|
||||
else if (opts->config == config_persistent)
|
||||
else if (opts->config == config_persistent ||
|
||||
opts->site_id != SITE_FALLBACK)
|
||||
opts->select->configured = 1;
|
||||
}
|
||||
break;
|
||||
@@ -782,34 +852,53 @@ static struct column *dev_table = COLUMN_ARRAY(
|
||||
);
|
||||
|
||||
static char *merge_str(const char *act, const char *pers, const char *ac,
|
||||
config_t config)
|
||||
config_t config, char **site, int max_sites)
|
||||
{
|
||||
char *str;
|
||||
size_t len;
|
||||
int i;
|
||||
struct util_list *str_list;
|
||||
|
||||
str_list = strlist_new();
|
||||
|
||||
act = act ? act : "-";
|
||||
pers = pers ? pers : "-";
|
||||
ac = ac ? ac : "-";
|
||||
|
||||
if (strcmp(act, pers) == 0 && strcmp(act, ac) == 0)
|
||||
if (strcmp(act, pers) == 0 && strcmp(act, ac) == 0 &&
|
||||
max_sites == 1)
|
||||
return misc_strdup(act);
|
||||
|
||||
len = strlen(act) + strlen(pers) + strlen(ac) + /* 3 * / + NUL */ 4;
|
||||
str = misc_malloc(len);
|
||||
|
||||
if (SCOPE_ACTIVE(config))
|
||||
strcat(str, act);
|
||||
strlist_add(str_list, act);
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
if (*str)
|
||||
strcat(str, "/");
|
||||
strcat(str, pers);
|
||||
if (global_site_id == SITE_FALLBACK) {
|
||||
strlist_add(str_list, pers);
|
||||
} else {
|
||||
strlist_add(str_list, site[global_site_id]);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (SCOPE_AUTOCONF(config)) {
|
||||
if (*str)
|
||||
strcat(str, "/");
|
||||
strcat(str, ac);
|
||||
strlist_add(str_list, ac);
|
||||
}
|
||||
|
||||
/* Do not show site-specific information when user specifically
|
||||
* asked for persistent configuration setting.
|
||||
*/
|
||||
if (SCOPE_PERSISTENT(config) && config != config_persistent) {
|
||||
for (i = 0; i <= max_sites; i++) {
|
||||
if (site[i])
|
||||
strlist_add(str_list, site[i]);
|
||||
else
|
||||
if (str_list)
|
||||
strlist_add(str_list, "-");
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
str = strlist_flatten(str_list, "/");
|
||||
strlist_free(str_list);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -848,12 +937,13 @@ static char *dev_table_get_modules(struct device *dev)
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *get_attr(struct device *dev, const char *name, config_t config)
|
||||
static char *get_attr(struct device *dev, const char *name, config_t config,
|
||||
int site_id)
|
||||
{
|
||||
struct setting_list *list;
|
||||
struct setting *s;
|
||||
|
||||
list = device_get_setting_list(dev, config);
|
||||
list = device_get_setting_list(dev, config, site_id);
|
||||
if (!list)
|
||||
return NULL;
|
||||
s = setting_list_find(list, name);
|
||||
@@ -868,30 +958,61 @@ static char *get_attr(struct device *dev, const char *name, config_t config)
|
||||
return misc_strdup(s->value);
|
||||
}
|
||||
|
||||
/* When lszdev --info, make sure that the output contains only the
|
||||
* valid site information. Check if the site is valid and then add
|
||||
* the new SITEn column. For SITE_FALLBACK, the PERSISTENT column will
|
||||
* be used.
|
||||
*/
|
||||
static bool is_site_show(int site_id, int current_site)
|
||||
{
|
||||
if (site_id == SITE_FALLBACK || site_id == current_site)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return string for attribute with specified @name for device @dev. */
|
||||
static char *dev_table_get_attr(struct device *dev, const char *attr,
|
||||
config_t config)
|
||||
{
|
||||
char *act = NULL, *pers = NULL, *ac = NULL, *str;
|
||||
const char *name;
|
||||
char *site[NUM_USER_SITES];
|
||||
int i;
|
||||
static int max_sites;
|
||||
|
||||
name = strchr(attr, ':');
|
||||
if (!name)
|
||||
return NULL;
|
||||
name++;
|
||||
|
||||
/* To get the default settings on any configuration, make sure that
|
||||
* the site_id is specified as SITE_FALLBACK. Any value of site_id
|
||||
* less than SITE_FALLBACK will endup providing site-specific attribute
|
||||
* settings.
|
||||
*/
|
||||
if (SCOPE_ACTIVE(config))
|
||||
act = get_attr(dev, name, config_active);
|
||||
if (SCOPE_PERSISTENT(config))
|
||||
pers = get_attr(dev, name, config_persistent);
|
||||
act = get_attr(dev, name, config_active, SITE_FALLBACK);
|
||||
if (SCOPE_PERSISTENT(config)) {
|
||||
pers = get_attr(dev, name, config_persistent, SITE_FALLBACK);
|
||||
for (i = 0; i < NUM_USER_SITES; i++)
|
||||
site[i] = get_attr(dev, name, config, i);
|
||||
}
|
||||
if (SCOPE_AUTOCONF(config))
|
||||
ac = get_attr(dev, name, config_autoconf);
|
||||
ac = get_attr(dev, name, config_autoconf, SITE_FALLBACK);
|
||||
|
||||
str = merge_str(act, pers, ac, config);
|
||||
for (i = 0; i < NUM_USER_SITES; i++)
|
||||
if (dev->site_specific[i].exists)
|
||||
max_sites = (max_sites < i) ? i : max_sites;
|
||||
|
||||
str = merge_str(act, pers, ac, config, site, max_sites);
|
||||
free(act);
|
||||
free(pers);
|
||||
free(ac);
|
||||
|
||||
if (SCOPE_PERSISTENT(config))
|
||||
for (i = 0; i < NUM_USER_SITES; i++)
|
||||
free(site[i]);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -938,9 +1059,9 @@ static char *dev_table_get_value(void *item, int id, const char *heading,
|
||||
return misc_strdup(YESNO(dev->active.exists ||
|
||||
dev->active.definable));
|
||||
case dev_pers:
|
||||
if (!dev->persistent.exists && dev->autoconf.exists)
|
||||
if (!is_dev_pers(dev) && dev->autoconf.exists)
|
||||
return misc_strdup("auto");
|
||||
return misc_strdup(YESNO(dev->persistent.exists));
|
||||
return get_site_from_pers(dev);
|
||||
case dev_auto:
|
||||
return misc_strdup(YESNO(dev->autoconf.exists));
|
||||
case dev_online:
|
||||
@@ -1032,11 +1153,17 @@ static struct util_list *dev_table_build(struct options *opts,
|
||||
|
||||
/* Only process existing devices. */
|
||||
active = dev->active.exists || dev->active.definable;
|
||||
persistent = dev->persistent.exists;
|
||||
persistent = (int)is_dev_pers(dev);
|
||||
autoconf = dev->autoconf.exists;
|
||||
if (!active && !persistent && !autoconf)
|
||||
continue;
|
||||
|
||||
/* with --site option, we consider only the persistent
|
||||
* configurations available on the specified site-id.
|
||||
* ignore active and autoconf here
|
||||
*/
|
||||
if (!persistent && global_site_id != SITE_FALLBACK)
|
||||
continue;
|
||||
ptrlist_add(devices, dev);
|
||||
}
|
||||
|
||||
@@ -1107,6 +1234,16 @@ enum settings_table_id {
|
||||
settings_active,
|
||||
settings_persistent,
|
||||
settings_autoconf,
|
||||
settings_site0,
|
||||
settings_site1,
|
||||
settings_site2,
|
||||
settings_site3,
|
||||
settings_site4,
|
||||
settings_site5,
|
||||
settings_site6,
|
||||
settings_site7,
|
||||
settings_site8,
|
||||
settings_site9,
|
||||
};
|
||||
|
||||
static struct column *settings_table = COLUMN_ARRAY(
|
||||
@@ -1114,17 +1251,29 @@ static struct column *settings_table = COLUMN_ARRAY(
|
||||
COLUMN("READONLY", align_left, settings_readonly, 1, ""),
|
||||
COLUMN("ACTIVE", align_left, settings_active, 1, ""),
|
||||
COLUMN("PERSISTENT", align_left, settings_persistent, 1, ""),
|
||||
COLUMN("AUTOCONF", align_left, settings_autoconf, 0, "")
|
||||
COLUMN("AUTOCONF", align_left, settings_autoconf, 0, ""),
|
||||
COLUMN("SITE0", align_left, settings_site0, 0, ""),
|
||||
COLUMN("SITE1", align_left, settings_site1, 0, ""),
|
||||
COLUMN("SITE2", align_left, settings_site2, 0, ""),
|
||||
COLUMN("SITE3", align_left, settings_site3, 0, ""),
|
||||
COLUMN("SITE4", align_left, settings_site4, 0, ""),
|
||||
COLUMN("SITE5", align_left, settings_site5, 0, ""),
|
||||
COLUMN("SITE6", align_left, settings_site6, 0, ""),
|
||||
COLUMN("SITE7", align_left, settings_site7, 0, ""),
|
||||
COLUMN("SITE8", align_left, settings_site8, 0, ""),
|
||||
COLUMN("SITE9", align_left, settings_site9, 0, "")
|
||||
);
|
||||
|
||||
static struct util_list *settings_table_build(struct setting_list *active,
|
||||
struct setting_list *persistent,
|
||||
struct setting_list *autoconf,
|
||||
struct device *dev,
|
||||
bool readonly)
|
||||
{
|
||||
struct util_list *names, *items;
|
||||
struct setting *s;
|
||||
struct strlist_node *str;
|
||||
int i;
|
||||
|
||||
/* Get a list of all configured attributes. */
|
||||
names = strlist_new();
|
||||
@@ -1139,6 +1288,11 @@ static struct util_list *settings_table_build(struct setting_list *active,
|
||||
if (persistent && !readonly) {
|
||||
util_list_iterate(&persistent->list, s)
|
||||
strlist_add(names, s->name);
|
||||
if (dev) {
|
||||
for (i = 0; i < NUM_SITES; i++)
|
||||
util_list_iterate(&dev->site_specific[i].settings->list, s)
|
||||
strlist_add(names, s->name);
|
||||
}
|
||||
}
|
||||
if (autoconf && !readonly) {
|
||||
util_list_iterate(&autoconf->list, s)
|
||||
@@ -1159,6 +1313,7 @@ struct settings_table_data {
|
||||
struct setting_list *active;
|
||||
struct setting_list *persistent;
|
||||
struct setting_list *autoconf;
|
||||
struct setting_list *sites[NUM_SITES];
|
||||
int pairs;
|
||||
bool readonly;
|
||||
};
|
||||
@@ -1184,6 +1339,37 @@ static char *settings_table_get_value(void *item, int id, const char *heading,
|
||||
case settings_autoconf:
|
||||
list = stdata->autoconf;
|
||||
break;
|
||||
case settings_site0:
|
||||
list = stdata->sites[0];
|
||||
break;
|
||||
case settings_site1:
|
||||
list = stdata->sites[1];
|
||||
break;
|
||||
case settings_site2:
|
||||
list = stdata->sites[2];
|
||||
break;
|
||||
case settings_site3:
|
||||
list = stdata->sites[3];
|
||||
break;
|
||||
case settings_site4:
|
||||
list = stdata->sites[4];
|
||||
break;
|
||||
case settings_site5:
|
||||
list = stdata->sites[5];
|
||||
break;
|
||||
case settings_site6:
|
||||
list = stdata->sites[6];
|
||||
break;
|
||||
case settings_site7:
|
||||
list = stdata->sites[7];
|
||||
break;
|
||||
case settings_site8:
|
||||
list = stdata->sites[8];
|
||||
break;
|
||||
case settings_site9:
|
||||
list = stdata->sites[9];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1208,16 +1394,18 @@ static char *settings_table_get_value(void *item, int id, const char *heading,
|
||||
static void settings_table_print(struct setting_list *active,
|
||||
struct setting_list *persistent,
|
||||
struct setting_list *autoconf,
|
||||
struct device *dev,
|
||||
struct options *opts, int ind, bool readonly,
|
||||
bool neednl)
|
||||
{
|
||||
struct util_list *items;
|
||||
struct settings_table_data data;
|
||||
int i;
|
||||
|
||||
items = settings_table_build(
|
||||
SCOPE_ACTIVE(opts->config) ? active : NULL,
|
||||
SCOPE_PERSISTENT(opts->config) ? persistent : NULL,
|
||||
autoconf, readonly);
|
||||
autoconf, dev, readonly);
|
||||
if (util_list_is_empty(items)) {
|
||||
if (!opts->pairs && !readonly)
|
||||
indent(ind, "%sNo settings found\n",
|
||||
@@ -1232,14 +1420,28 @@ static void settings_table_print(struct setting_list *active,
|
||||
table_set_default(settings_table, settings_active,
|
||||
SCOPE_ACTIVE(opts->config));
|
||||
table_set_default(settings_table, settings_persistent,
|
||||
is_site_show(opts->site_id, SITE_FALLBACK) &&
|
||||
SCOPE_PERSISTENT(opts->config) && !readonly ? 1 : 0);
|
||||
table_set_default(settings_table, settings_autoconf,
|
||||
(SCOPE_AUTOCONF(opts->config) || autoconf) &&
|
||||
!readonly ? 1 : 0);
|
||||
if (dev) {
|
||||
for (i = 0; i < NUM_USER_SITES; i++)
|
||||
table_set_default(settings_table, settings_site0 + i,
|
||||
SCOPE_PERSISTENT(opts->config) &&
|
||||
is_site_show(opts->site_id, i) &&
|
||||
dev->site_specific[i].exists &&
|
||||
!readonly ? 1 : 0);
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++)
|
||||
data.sites[i] = dev->site_specific[i].settings;
|
||||
}
|
||||
|
||||
data.active = active;
|
||||
data.persistent = persistent;
|
||||
data.autoconf = autoconf;
|
||||
data.pairs = opts->pairs;
|
||||
|
||||
table_print(settings_table, settings_table_get_value, &data, items,
|
||||
NULL, 1, opts->pairs, ind, 0);
|
||||
|
||||
@@ -1320,7 +1522,7 @@ static exit_code_t do_info_type(struct options *opts)
|
||||
}
|
||||
} else {
|
||||
settings_table_print(dt->active_settings,
|
||||
dt->persistent_settings, NULL, opts,
|
||||
dt->persistent_settings, NULL, NULL, opts,
|
||||
INFO_INDENT, false, false);
|
||||
}
|
||||
|
||||
@@ -1346,7 +1548,7 @@ static void do_list_columns(struct options *opts)
|
||||
static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
char *names, *bdevs, *cdevs, *ndevs, *modules, *online, *path, *key;
|
||||
char *names, *bdevs, *cdevs, *ndevs, *modules, *online, *path, *key, *sites;
|
||||
const char *id, *type, *exists, *persist, *ac, *prefix;
|
||||
struct util_list *resources, *errors;
|
||||
struct strlist_node *s;
|
||||
@@ -1367,6 +1569,7 @@ static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
exists = YESNO(dev->active.exists || dev->active.definable);
|
||||
persist = YESNO(dev->persistent.exists);
|
||||
ac = YESNO(dev->autoconf.exists);
|
||||
sites = device_get_sites(dev);
|
||||
|
||||
/* Print information. */
|
||||
if (opts->pairs) {
|
||||
@@ -1384,6 +1587,9 @@ static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
print_pair("ONLINE", online);
|
||||
print_pair("EXISTS", exists);
|
||||
print_pair("PERSISTENT", persist);
|
||||
if (*sites)
|
||||
print_pair("SITES", sites);
|
||||
|
||||
if (SCOPE_AUTOCONF(opts->config) || dev->autoconf.exists)
|
||||
print_pair("AUTOCONF", ac);
|
||||
if (errors) {
|
||||
@@ -1413,6 +1619,9 @@ static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
print_info("Persistent", persist);
|
||||
if (SCOPE_AUTOCONF(opts->config) || dev->autoconf.exists)
|
||||
print_info("Auto-configured", ac);
|
||||
if (*sites)
|
||||
print_info("Sites", sites);
|
||||
|
||||
if (errors) {
|
||||
first = true;
|
||||
util_list_iterate(errors, s) {
|
||||
@@ -1453,17 +1662,17 @@ static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
}
|
||||
|
||||
settings_table_print(dev->active.exists ? dev->active.settings : NULL,
|
||||
dev->persistent.exists ? dev->persistent.settings :
|
||||
is_dev_pers(dev) ? dev->persistent.settings :
|
||||
NULL,
|
||||
dev->autoconf.exists ? dev->autoconf.settings :
|
||||
NULL,
|
||||
NULL, dev,
|
||||
opts, INFO_INDENT, false, !opts->pairs);
|
||||
|
||||
settings_table_print(dev->active.exists ? dev->active.settings : NULL,
|
||||
dev->persistent.exists ? dev->persistent.settings :
|
||||
is_dev_pers(dev) ? dev->persistent.settings :
|
||||
NULL,
|
||||
dev->autoconf.exists ? dev->autoconf.settings :
|
||||
NULL,
|
||||
NULL, dev,
|
||||
opts, INFO_INDENT, true, !opts->pairs);
|
||||
|
||||
strlist_free(errors);
|
||||
@@ -1474,6 +1683,7 @@ static void do_info_one_device(struct device *dev, struct options *opts)
|
||||
free(cdevs);
|
||||
free(bdevs);
|
||||
free(names);
|
||||
free(sites);
|
||||
}
|
||||
|
||||
/* Perform --info on devices. */
|
||||
@@ -1518,10 +1728,18 @@ static exit_code_t do_info_devices(struct options *opts)
|
||||
|
||||
/* Only process existing devices. */
|
||||
active = dev->active.exists || dev->active.definable;
|
||||
persistent = dev->persistent.exists;
|
||||
persistent = (int)is_dev_pers(dev);
|
||||
autoconf = dev->autoconf.exists;
|
||||
if (!active && !persistent && !autoconf)
|
||||
continue;
|
||||
|
||||
/* with --site option, we consider only the persistent
|
||||
* configurations available on the specified site-id.
|
||||
* ignore active and autoconf here
|
||||
*/
|
||||
if (!persistent && global_site_id != SITE_FALLBACK)
|
||||
continue;
|
||||
|
||||
if (found > 0)
|
||||
printf("\n");
|
||||
do_info_one_device(dev, opts);
|
||||
@@ -1558,6 +1776,7 @@ int main(int argc, char *argv[])
|
||||
/* Set globals. */
|
||||
verbose = opts.verbose;
|
||||
quiet = opts.quiet;
|
||||
global_site_id = opts.site_id;
|
||||
path_set_base(opts.base);
|
||||
if (opts.pairs)
|
||||
set_stdout_data();
|
||||
|
||||
@@ -42,5 +42,6 @@ OPTIONS
|
||||
--base PATH Use PATH as base for accessing files
|
||||
--pairs Produce output in KEY="VALUE" format
|
||||
--auto-conf Only show auto-configuration data
|
||||
-s, --site ID Only show data configured for the specified site
|
||||
-V, --verbose Print additional run-time information
|
||||
-q, --quiet Print only minimal run-time information
|
||||
|
||||
@@ -1695,3 +1695,20 @@ char *misc_strrstr(const char *haystack, const char *needle)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Check if the site mentioned is valid */
|
||||
bool is_valid_site(const char *str)
|
||||
{
|
||||
char *ptr = NULL;
|
||||
long site_id;
|
||||
|
||||
site_id = strtol(str, &ptr, 10);
|
||||
|
||||
if (*ptr)
|
||||
return false;
|
||||
|
||||
if (site_id < 0 || site_id >= NUM_USER_SITES)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -404,3 +404,13 @@ char *path_get_bus_attr(const char *bus, const char *attr)
|
||||
{
|
||||
return path_get("/sys/bus/%s/%s", bus, attr);
|
||||
}
|
||||
|
||||
/* Return path will be with ZDEV_PREFIX oon PATH_UDEV_RULES */
|
||||
char *path_get_zdev_rule(const char *name)
|
||||
{
|
||||
const char *path = PATH_UDEV_RULES;
|
||||
|
||||
/* Create file. */
|
||||
return path_get("%s/%s-%s%s", path,
|
||||
ZDEV_PREFIX, name, UDEV_SUFFIX);
|
||||
}
|
||||
|
||||
@@ -83,23 +83,58 @@ static bool is_zdev_early_0(struct selected_dev_node *sel)
|
||||
{
|
||||
struct setting *s;
|
||||
struct device *dev;
|
||||
int i;
|
||||
|
||||
dev = device_list_find(sel->st->devices, sel->id, NULL);
|
||||
if (!dev)
|
||||
return false;
|
||||
s = setting_list_find(dev->persistent.settings,
|
||||
internal_attr_early.name);
|
||||
if (!s)
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++) {
|
||||
s = setting_list_find(dev->site_specific[i].settings,
|
||||
internal_attr_early.name);
|
||||
if (!s)
|
||||
return false;
|
||||
if (s->specified && strcmp(s->value, "0") == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* When site-specific settings has zdev:early configured, it is not
|
||||
* detected with the --by-attr functionality of select_devices. Instead
|
||||
* we will have to iterate through each configuration and check if there
|
||||
* is a zdev:early set in the device. This is usually applicable only in
|
||||
* the case of deconfigure-all command, because, for all other commands
|
||||
* we copy the settings-in-action to the persistent settings and do the
|
||||
* processing there.
|
||||
*/
|
||||
static bool is_zdev_early_site(struct selected_dev_node *sel)
|
||||
{
|
||||
struct setting *s;
|
||||
struct device *dev;
|
||||
int i;
|
||||
|
||||
if (global_site_id != SITE_FALLBACK)
|
||||
return false;
|
||||
if (s->specified && strcmp(s->value, "0") == 0)
|
||||
return true;
|
||||
|
||||
dev = device_list_find(sel->st->devices, sel->id, NULL);
|
||||
if (!dev)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < NUM_SITES; i++) {
|
||||
s = setting_list_find(dev->site_specific[i].settings,
|
||||
internal_attr_early.name);
|
||||
if (s)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Determine if initial RAM-disk needs updating. If so, run the corresponding
|
||||
* scripts if available. */
|
||||
exit_code_t initrd_check(bool all_pers)
|
||||
exit_code_t initrd_check(bool all_pers, int site_validate)
|
||||
{
|
||||
struct util_list *selected, *params, *mod = strlist_new();
|
||||
struct selected_dev_node *sel;
|
||||
@@ -142,7 +177,12 @@ exit_code_t initrd_check(bool all_pers)
|
||||
}
|
||||
/* Finally add devices with zdev:early=1. */
|
||||
select = select_opts_new();
|
||||
strlist_add(&select->by_attr, "%s=1", INTERNAL_ATTR_EARLY);
|
||||
/* If we need information from multiple site, we cannot use the by-attr
|
||||
* functionality of select_devices function. Instead we have to iterate
|
||||
* through individual devices and site settings to find the key.
|
||||
*/
|
||||
if (!site_validate)
|
||||
strlist_add(&select->by_attr, "%s=1", INTERNAL_ATTR_EARLY);
|
||||
select_devices(select, selected, 1, 0, 0,
|
||||
config_active | config_persistent, scope_mandatory,
|
||||
err_ignore);
|
||||
@@ -162,7 +202,12 @@ check_mod:
|
||||
/* Check devices. */
|
||||
dev = device_list_find(sel->st->devices, sel->id, NULL);
|
||||
if (dev && dev->persistent.exists &&
|
||||
(device_needs_writing(dev, config_persistent) || force)) {
|
||||
(device_needs_writing(dev, config_persistent) || force) &&
|
||||
!site_validate) {
|
||||
strlist_add(mod, "%s %s", dev->subtype->devname,
|
||||
dev->id);
|
||||
} else if (site_validate && is_zdev_early_site(sel) &&
|
||||
device_needs_writing(dev, config_persistent)) {
|
||||
strlist_add(mod, "%s %s", dev->subtype->devname,
|
||||
dev->id);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "path.h"
|
||||
#include "setting.h"
|
||||
#include "udev.h"
|
||||
#include "zdev_id.h"
|
||||
|
||||
int udev_need_settle = 0;
|
||||
int udev_no_settle;
|
||||
@@ -137,3 +138,61 @@ void udev_add_internal_from_entry(struct setting_list *list,
|
||||
out:
|
||||
free(copy);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the UDEV rule that determines the active SITE_ID
|
||||
*/
|
||||
exit_code_t udev_write_site_rule(void)
|
||||
{
|
||||
char *path, *name = "zdev-id";
|
||||
exit_code_t rc = EXIT_OK;
|
||||
FILE *fd;
|
||||
|
||||
/* Create file. */
|
||||
path = path_get_zdev_rule(name);
|
||||
|
||||
if (!util_path_exists(path)) {
|
||||
rc = path_create(path);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = misc_fopen(path, "w");
|
||||
if (!fd) {
|
||||
error("Could not write to file %s: %s\n", path,
|
||||
strerror(errno));
|
||||
rc = EXIT_RUNTIME_ERROR;
|
||||
goto out;
|
||||
}
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
fprintf(fd, "\n");
|
||||
/* We have to make sure that we are not trying to determine the SITE_ID
|
||||
* for all the corresponding uevent. Check if the SITE_ID is already
|
||||
* determined by reading the file /run/zdev_site_id; and if it is not
|
||||
* available, execute the zdev_id program and determine the SITE_ID and
|
||||
* create the new zdev_site_id file for next run.
|
||||
*/
|
||||
fprintf(fd, "TEST!=\"%s\", IMPORT{program}="
|
||||
"\"%s/zdev_id\",GOTO=\"end\"\n", ZDEV_SITE_ID_FILE,
|
||||
TOOLS_LIBDIR);
|
||||
fprintf(fd, "IMPORT{file}=\"%s\"\n", ZDEV_SITE_ID_FILE);
|
||||
fprintf(fd, "LABEL=\"end\"\n");
|
||||
|
||||
if (misc_fclose(fd))
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
|
||||
out:
|
||||
free(path);
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool is_legacy_rule(struct util_udev_file *file)
|
||||
{
|
||||
struct util_udev_line_node *line;
|
||||
|
||||
util_list_iterate(&file->lines, line)
|
||||
if (starts_with(line->line, SITE_BLOCK_START))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "udev.h"
|
||||
#include "udev_ccw.h"
|
||||
|
||||
#define SITE_FALLBACK_SUFFIX "fb"
|
||||
|
||||
/* Check if a udev rule for the specified ccw device exists. */
|
||||
bool udev_ccw_exists(const char *type, const char *id, bool autoconf)
|
||||
{
|
||||
@@ -79,10 +81,68 @@ out:
|
||||
free(copy);
|
||||
}
|
||||
|
||||
static void get_site_from_line(char *line, int *site, bool *in_site)
|
||||
{
|
||||
char site_str[2];
|
||||
|
||||
if (starts_with(line, SITE_BLOCK_START)) {
|
||||
memcpy(site_str, line + sizeof(SITE_BLOCK_START), 2);
|
||||
if (strcmp(site_str, "fb") != 0)
|
||||
*site = atoi(site_str);
|
||||
else
|
||||
*site = SITE_FALLBACK;
|
||||
*in_site = true;
|
||||
} else if (starts_with(line, SITE_BLOCK_END)) {
|
||||
memcpy(site_str, line + sizeof(SITE_BLOCK_END), 2);
|
||||
if (strcmp(site_str, "fb") != 0)
|
||||
*site = atoi(site_str);
|
||||
else
|
||||
*site = SITE_FALLBACK;
|
||||
*in_site = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void dev_state_copy(struct device_state *dst, struct device_state *src)
|
||||
{
|
||||
setting_list_free(dst->settings);
|
||||
dst->settings = setting_list_copy(src->settings);
|
||||
dst->exists = src->exists;
|
||||
dst->modified = src->modified;
|
||||
dst->deconfigured = src->deconfigured;
|
||||
dst->blacklisted = src->blacklisted;
|
||||
dst->definable = src->definable;
|
||||
}
|
||||
|
||||
/* Extract per-site CCW device settings from a CCW device udev rule file. */
|
||||
static void _udev_file_get_settings_new(struct util_udev_file *file,
|
||||
struct attrib **attribs,
|
||||
struct device *dev)
|
||||
{
|
||||
struct util_udev_line_node *line;
|
||||
struct util_udev_entry_node *entry;
|
||||
int site_id = SITE_FALLBACK;
|
||||
struct setting_list *list;
|
||||
bool in_site = false;
|
||||
|
||||
util_list_iterate(&file->lines, line) {
|
||||
get_site_from_line(line->line, &site_id, &in_site);
|
||||
if (!in_site || site_id >= NUM_SITES || site_id < 0)
|
||||
continue;
|
||||
entry = util_list_start(&line->entries);
|
||||
if (!entry)
|
||||
continue;
|
||||
list = dev->site_specific[site_id].settings;
|
||||
add_setting_from_entry(list, entry, attribs);
|
||||
dev->site_specific[site_id].exists = 1;
|
||||
if (site_id == global_site_id)
|
||||
dev_state_copy(&dev->persistent, &dev->site_specific[site_id]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract CCW device settings from a CCW device udev rule file. */
|
||||
static void udev_file_get_settings(struct util_udev_file *file,
|
||||
struct attrib **attribs,
|
||||
struct setting_list *list)
|
||||
static void _udev_file_get_settings_legacy(struct util_udev_file *file,
|
||||
struct attrib **attribs,
|
||||
struct setting_list *list)
|
||||
{
|
||||
struct util_udev_line_node *line;
|
||||
struct util_udev_entry_node *entry;
|
||||
@@ -95,6 +155,22 @@ static void udev_file_get_settings(struct util_udev_file *file,
|
||||
}
|
||||
}
|
||||
|
||||
static void udev_file_get_settings(struct util_udev_file *file,
|
||||
struct attrib **attribs,
|
||||
struct device *dev,
|
||||
bool autoconf)
|
||||
{
|
||||
struct device_state *state = autoconf ? &dev->autoconf :
|
||||
&dev->persistent;
|
||||
|
||||
if (is_legacy_rule(file) || autoconf) {
|
||||
_udev_file_get_settings_legacy(file, attribs, state->settings);
|
||||
state->exists = 1;
|
||||
} else {
|
||||
_udev_file_get_settings_new(file, attribs, dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the persistent configuration of a CCW device from a udev rule. */
|
||||
exit_code_t udev_ccw_read_device(struct device *dev, bool autoconf)
|
||||
{
|
||||
@@ -113,8 +189,7 @@ exit_code_t udev_ccw_read_device(struct device *dev, bool autoconf)
|
||||
warn_once("Warning: Invalid udev rule: %s\n", path);
|
||||
state->exists = 0;
|
||||
} else {
|
||||
udev_file_get_settings(file, st->dev_attribs, state->settings);
|
||||
state->exists = 1;
|
||||
udev_file_get_settings(file, st->dev_attribs, dev, autoconf);
|
||||
}
|
||||
util_udev_free_file(file);
|
||||
|
||||
@@ -141,8 +216,32 @@ static char *get_label_id(const char *prefix, const char *type,
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Write the persistent configuration of a CCW device to a udev rule. */
|
||||
exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
|
||||
static void write_attr_to_file(FILE *fd, struct device_state *state, const char *id)
|
||||
{
|
||||
struct ptrlist_node *p;
|
||||
struct setting *s;
|
||||
struct util_list *list = NULL;
|
||||
|
||||
/* Apply attributes in correct order. */
|
||||
list = setting_list_get_sorted(state->settings);
|
||||
|
||||
util_list_iterate(list, p) {
|
||||
s = p->ptr;
|
||||
if (s->removed)
|
||||
continue;
|
||||
if ((s->attrib && s->attrib->internal) ||
|
||||
internal_by_name(s->name)) {
|
||||
fprintf(fd, "ENV{zdev_%s}=\"%s\"\n",
|
||||
internal_get_name(s->name), s->value);
|
||||
} else {
|
||||
fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name,
|
||||
s->value);
|
||||
}
|
||||
}
|
||||
ptrlist_free(list, 0);
|
||||
}
|
||||
|
||||
static exit_code_t udev_ccw_write_device_legacy(struct device *dev, bool autoconf)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct ccw_subtype_data *data = st->data;
|
||||
@@ -150,22 +249,16 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
|
||||
struct device_state *state = autoconf ? &dev->autoconf :
|
||||
&dev->persistent;
|
||||
char *path, *cfg_label = NULL, *end_label = NULL;
|
||||
struct util_list *list;
|
||||
struct ptrlist_node *p;
|
||||
struct setting *s;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
FILE *fd;
|
||||
|
||||
if (!state->exists)
|
||||
return udev_remove_rule(type, id, autoconf);
|
||||
return udev_remove_rule(type, id, true);
|
||||
|
||||
cfg_label = get_label_id("cfg", type, id);
|
||||
end_label = get_label_id("end", type, id);
|
||||
|
||||
/* Apply attributes in correct order. */
|
||||
list = setting_list_get_sorted(state->settings);
|
||||
|
||||
path = path_get_udev_rule(type, id, autoconf);
|
||||
path = path_get_udev_rule(type, id, true);
|
||||
debug("Writing %s udev rule file %s\n", type, path);
|
||||
if (!util_path_exists(path)) {
|
||||
rc = path_create(path);
|
||||
@@ -196,22 +289,8 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
|
||||
}
|
||||
fprintf(fd, "GOTO=\"%s\"\n", end_label);
|
||||
fprintf(fd, "\n");
|
||||
fprintf(fd, "LABEL=\"%s\"\n", cfg_label);
|
||||
|
||||
/* Write settings. */
|
||||
util_list_iterate(list, p) {
|
||||
s = p->ptr;
|
||||
if (s->removed)
|
||||
continue;
|
||||
if ((s->attrib && s->attrib->internal) ||
|
||||
internal_by_name(s->name)) {
|
||||
fprintf(fd, "ENV{zdev_%s}=\"%s\"\n",
|
||||
internal_get_name(s->name), s->value);
|
||||
} else {
|
||||
fprintf(fd, "ATTR{[ccw/%s]%s}=\"%s\"\n", id, s->name,
|
||||
s->value);
|
||||
}
|
||||
}
|
||||
write_attr_to_file(fd, state, id);
|
||||
|
||||
/* Write udev rule epilog. */
|
||||
fprintf(fd, "\n");
|
||||
@@ -221,7 +300,6 @@ exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
|
||||
out:
|
||||
ptrlist_free(list, 0);
|
||||
free(end_label);
|
||||
free(cfg_label);
|
||||
free(path);
|
||||
@@ -229,6 +307,150 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Write the persistent configuration of a CCW device to a udev rule. */
|
||||
static exit_code_t udev_ccw_write_device_new(struct device *dev)
|
||||
{
|
||||
struct subtype *st = dev->subtype;
|
||||
struct ccw_subtype_data *data = st->data;
|
||||
const char *type = st->name, *drv = data->any_driver ? "*" : data->ccwdrv, *id = dev->id;
|
||||
char *path, *cfg_label = NULL, *end_label = NULL;
|
||||
exit_code_t rc = EXIT_OK;
|
||||
FILE *fd;
|
||||
int i, configured = 0;
|
||||
|
||||
/*
|
||||
* Remove the previous file; The new udev rule will be created based on the
|
||||
* current configuration settings
|
||||
*/
|
||||
udev_remove_rule(type, id, 0);
|
||||
|
||||
/*
|
||||
* Copy the dev->persistent to the site_specific array; While writing the new
|
||||
* udev-rule we consider the site_specific array only
|
||||
*/
|
||||
|
||||
dev_state_copy(&dev->site_specific[global_site_id], &dev->persistent);
|
||||
for (i = 0; i < NUM_SITES; i++) {
|
||||
configured += dev->site_specific[i].exists;
|
||||
configured -= dev->site_specific[i].deconfigured;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is no configuration settings available, do not create the new
|
||||
* udev-rule file; exit here
|
||||
*/
|
||||
if (!configured)
|
||||
return rc;
|
||||
|
||||
cfg_label = get_label_id("cfg", type, id);
|
||||
end_label = get_label_id("end", type, id);
|
||||
|
||||
path = path_get_udev_rule(type, id, false);
|
||||
debug("Writing %s udev rule file %s\n", type, path);
|
||||
if (!util_path_exists(path)) {
|
||||
rc = path_create(path);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = misc_fopen(path, "w");
|
||||
if (!fd) {
|
||||
error("Could not write to file %s: %s\n", path,
|
||||
strerror(errno));
|
||||
rc = EXIT_RUNTIME_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write udev rule prolog. */
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
|
||||
if (drv) {
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", "
|
||||
"KERNEL==\"%s\", DRIVER==\"%s\", GOTO=\"%s\"\n", id,
|
||||
drv, cfg_label);
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"drivers\", "
|
||||
"KERNEL==\"%s\", TEST==\"[ccw/%s]\", "
|
||||
"GOTO=\"%s\"\n", drv, id, cfg_label);
|
||||
} else {
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"ccw\", "
|
||||
"KERNEL==\"%s\", GOTO=\"%s\"\n", id, cfg_label);
|
||||
}
|
||||
fprintf(fd, "GOTO=\"%s\"\n", end_label);
|
||||
fprintf(fd, "\n");
|
||||
|
||||
fprintf(fd, "LABEL=\"%s\"\n", cfg_label);
|
||||
/* site comparison block */
|
||||
for (i = 0; i < NUM_USER_SITES; i++) {
|
||||
if (dev->site_specific[i].exists &&
|
||||
!dev->site_specific[i].deconfigured) {
|
||||
fprintf(fd, "ENV{ZDEV_SITE_ID}==\"%d\",GOTO=\"%s_site%d\"\n", i,
|
||||
cfg_label, i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a generic configuration available, then use that setting as a
|
||||
* fail-over incase of no site-id information in LOADPARM
|
||||
*/
|
||||
if (dev->site_specific[SITE_FALLBACK].exists &&
|
||||
!dev->site_specific[SITE_FALLBACK].deconfigured)
|
||||
fprintf(fd, "GOTO=\"%s_site_fb\"\n", cfg_label);
|
||||
else
|
||||
fprintf(fd, "GOTO=\"%s\"\n", end_label);
|
||||
fprintf(fd, "\n");
|
||||
|
||||
/* Write the site blocks for all the available configurations */
|
||||
for (i = 0; i < NUM_USER_SITES; i++) {
|
||||
if (dev->site_specific[i].exists &&
|
||||
!dev->site_specific[i].deconfigured) {
|
||||
fprintf(fd, "# site_start_%d\n", i);
|
||||
fprintf(fd, "LABEL=\"%s_site%d\"\n", cfg_label, i);
|
||||
|
||||
write_attr_to_file(fd, &dev->site_specific[i], id);
|
||||
/* Write udev rule epilog. */
|
||||
fprintf(fd, "GOTO=\"%s\"\n", end_label);
|
||||
fprintf(fd, "# site_end_%d\n", i);
|
||||
fprintf(fd, "\n");
|
||||
}
|
||||
}
|
||||
if (dev->site_specific[SITE_FALLBACK].exists &&
|
||||
!dev->site_specific[SITE_FALLBACK].deconfigured) {
|
||||
fprintf(fd, "# site_start_fb\n");
|
||||
fprintf(fd, "LABEL=\"%s_site_fb\"\n", cfg_label);
|
||||
|
||||
write_attr_to_file(fd, &dev->site_specific[i], id);
|
||||
/* Write udev rule epilog. */
|
||||
fprintf(fd, "GOTO=\"%s\"\n", end_label);
|
||||
fprintf(fd, "# site_end_fb\n");
|
||||
fprintf(fd, "\n");
|
||||
}
|
||||
|
||||
/* Write udev rule epilog. */
|
||||
fprintf(fd, "\n");
|
||||
fprintf(fd, "LABEL=\"%s\"\n", end_label);
|
||||
|
||||
if (misc_fclose(fd))
|
||||
warn("Could not close file %s: %s\n", path, strerror(errno));
|
||||
|
||||
rc = udev_write_site_rule();
|
||||
if (rc)
|
||||
goto out;
|
||||
out:
|
||||
free(end_label);
|
||||
free(cfg_label);
|
||||
free(path);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
exit_code_t udev_ccw_write_device(struct device *dev, bool autoconf)
|
||||
{
|
||||
if (autoconf)
|
||||
return udev_ccw_write_device_legacy(dev, autoconf);
|
||||
else
|
||||
return udev_ccw_write_device_new(dev);
|
||||
}
|
||||
|
||||
#define MARKER "echo free "
|
||||
|
||||
static char *read_cio_ignore(const char *path)
|
||||
@@ -300,6 +522,7 @@ exit_code_t udev_ccw_write_cio_ignore(const char *id_list, bool autoconf)
|
||||
|
||||
/* Write udev rule. */
|
||||
fprintf(fd, "# Generated by chzdev\n");
|
||||
|
||||
fprintf(fd, "ACTION==\"add\", SUBSYSTEM==\"subsystem\", "
|
||||
"KERNEL==\"ccw\", RUN{program}+=\"/bin/sh -c "
|
||||
"'echo free %s > /proc/cio_ignore'\"\n", id_list);
|
||||
|
||||
@@ -11,9 +11,17 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "zdev_id.h"
|
||||
|
||||
#define SYSINFO "/proc/sysinfo"
|
||||
#define CMDLINE "/proc/cmdline"
|
||||
#define LOADPARM "/sys/firmware/ipl/loadparm"
|
||||
#define IPL_DEV_ID "/sys/firmware/ipl/device"
|
||||
#define IPL_DEV_TYPE "/sys/firmware/ipl/ipl_type"
|
||||
#define SITE_FALLBACK 10
|
||||
|
||||
#define WHITESPACE " \t\n"
|
||||
|
||||
static void array_add(char ***array_p, int *num_p, const char *str)
|
||||
@@ -140,15 +148,139 @@ out:
|
||||
printf("ZDEV_NO_AUTO=%d\n", no_auto);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the erraneous loadparm provides a site_id which is not valid,
|
||||
* default it to the common-site id.
|
||||
*/
|
||||
static int validate_site(int site_id)
|
||||
{
|
||||
if (site_id < SITE_FALLBACK)
|
||||
return site_id;
|
||||
return SITE_FALLBACK;
|
||||
}
|
||||
|
||||
/* get the ipl device and extract the SSID */
|
||||
static int get_site_id(void)
|
||||
{
|
||||
FILE *fd;
|
||||
char *line;
|
||||
size_t n;
|
||||
int site_id = SITE_FALLBACK, ssid;
|
||||
|
||||
fd = fopen(IPL_DEV_ID, "r");
|
||||
if (!fd)
|
||||
goto fail_safe;
|
||||
|
||||
if (getline(&line, &n, fd) == -1)
|
||||
goto out;
|
||||
|
||||
if (sscanf(line, "%*d.%d.%*d", &ssid) == 1)
|
||||
site_id = validate_site(ssid);
|
||||
|
||||
out:
|
||||
free(line);
|
||||
fclose(fd);
|
||||
fail_safe:
|
||||
return site_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the ipl type and see if it belongs to ccw or zfcp, if different,
|
||||
* default it to common-site.
|
||||
*/
|
||||
static int read_ssid(void)
|
||||
{
|
||||
FILE *fd;
|
||||
char *line = NULL;
|
||||
size_t n;
|
||||
int site_id = SITE_FALLBACK;
|
||||
|
||||
fd = fopen(IPL_DEV_TYPE, "r");
|
||||
if (!fd)
|
||||
goto out;
|
||||
|
||||
if (getline(&line, &n, fd) != -1) {
|
||||
if (strcmp(line, "ccw") == 0 || strcmp(line, "zfcp"))
|
||||
site_id = get_site_id();
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(fd);
|
||||
out:
|
||||
return site_id;
|
||||
}
|
||||
|
||||
static void write_zdev_site_id(int site_id)
|
||||
{
|
||||
FILE *fd;
|
||||
|
||||
fd = fopen(ZDEV_SITE_ID_FILE, "w");
|
||||
if (!fd)
|
||||
err(1, "Could not write to zdev_site_id file");
|
||||
if (site_id == SITE_FALLBACK)
|
||||
fprintf(fd, "ZDEV_SITE_ID=\n");
|
||||
else
|
||||
fprintf(fd, "ZDEV_SITE_ID=%d\n", site_id);
|
||||
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
/* Read the loadparm and extract the current site_id.
|
||||
* loadparm can contains either Sn or "Ss". Sn indicate the site_id, where
|
||||
* 'n' is the integer which could be one of the valid site_ids from 0 to 9.
|
||||
* When loadparm value is "Ss", zdev_id extracts the site_id from the SSID
|
||||
* of the current ipl device. For ccw and zfcp devices, the current ipl
|
||||
* device-id can be found at /sys/firmware/ipl/device.
|
||||
* For all other invalid cases, set ZDEV_SITE_ID to NULL.
|
||||
*/
|
||||
|
||||
static void process_loadparm(const char *filename)
|
||||
{
|
||||
size_t n;
|
||||
FILE *fd;
|
||||
char *line = NULL, *substr;
|
||||
int site_id = SITE_FALLBACK;
|
||||
|
||||
fd = fopen(filename, "r");
|
||||
if (!fd)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We expect the value here to be either SX, where X is the site-id
|
||||
* or SS,in which case, the site-id will be derived from the SSID
|
||||
*/
|
||||
if ((getline(&line, &n, fd)) != -1) {
|
||||
substr = strchr(line, 'S');
|
||||
if (!substr)
|
||||
goto out;
|
||||
|
||||
if (isdigit(substr[1]))
|
||||
site_id = validate_site(atoi(&substr[1]));
|
||||
else if (substr[1] == 'S')
|
||||
site_id = read_ssid();
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(fd);
|
||||
out:
|
||||
write_zdev_site_id(site_id);
|
||||
if (site_id == SITE_FALLBACK)
|
||||
printf("ZDEV_SITE_ID=\n");
|
||||
else
|
||||
printf("ZDEV_SITE_ID=%d\n", site_id);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *sysinfo, *cmdline;
|
||||
char *sysinfo, *cmdline, *loadparm;
|
||||
|
||||
sysinfo = argc < 2 ? SYSINFO : argv[1];
|
||||
cmdline = argc < 3 ? CMDLINE : argv[2];
|
||||
loadparm = argc < 4 ? LOADPARM : argv[3];
|
||||
|
||||
process_sysinfo(sysinfo);
|
||||
process_cmdline(cmdline);
|
||||
process_loadparm(loadparm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
106
zdump/Makefile
106
zdump/Makefile
@@ -20,31 +20,52 @@ check_dep_fuse:
|
||||
"-DFUSE_USE_VERSION=30")
|
||||
endif
|
||||
|
||||
#
|
||||
# HAVE_ZLIB: Allow skip zgetdump build, when no zlib-devel is available
|
||||
#
|
||||
ifeq (${HAVE_ZLIB},0)
|
||||
.detect_openssl.dep.c:
|
||||
echo "#include <openssl/evp.h>" > $@
|
||||
echo "#if OPENSSL_VERSION_NUMBER < 0x10100000L" >> $@
|
||||
echo " #error At least OpenSSL version 1.1.0 is required" >> $@
|
||||
echo "#endif" >> $@
|
||||
echo "static void __attribute__((unused)) test(void) {" >> $@
|
||||
echo " EVP_MD_CTX *ctx = EVP_MD_CTX_new();" >> $@
|
||||
echo " EVP_MD_CTX_free(ctx);" >> $@
|
||||
echo "}" >> $@
|
||||
|
||||
all:
|
||||
$(SKIP) HAVE_ZLIB=0
|
||||
|
||||
install:
|
||||
$(SKIP) HAVE_ZLIB=0
|
||||
|
||||
else
|
||||
|
||||
check_dep_zlib:
|
||||
.check_dep_zgetdump: .detect_openssl.dep.c check_dep_fuse
|
||||
$(call check_dep, \
|
||||
"zgetdump", \
|
||||
"zlib.h", \
|
||||
"zlib-devel or libz-dev", \
|
||||
"HAVE_ZLIB=0")
|
||||
"zgetdump", \
|
||||
"zlib.h", \
|
||||
"zlib-devel or libz-dev", \
|
||||
"HAVE_ZLIB=0")
|
||||
$(call check_dep, \
|
||||
"zgetdump", \
|
||||
"glib.h", \
|
||||
"glib2-devel / libglib2.0-dev", \
|
||||
"HAVE_GLIB2=0")
|
||||
$(call check_dep, \
|
||||
"zgetdump", \
|
||||
$<, \
|
||||
"openssl-devel / libssl-dev version >= 1.1.0", \
|
||||
"HAVE_OPENSSL=0", \
|
||||
"-I.")
|
||||
touch $@
|
||||
|
||||
all: check_dep_fuse check_dep_zlib zgetdump
|
||||
#
|
||||
# Allow skip of zgetdump build, when no zlib-devel, openssl-devel, glib2-devel is available
|
||||
#
|
||||
BUILD_TARGETS :=skip-zgetdump
|
||||
INSTALL_TARGETS := skip-zgetdump
|
||||
ifneq (${HAVE_ZLIB},0)
|
||||
ifneq (${HAVE_OPENSSL},0)
|
||||
ifneq (${HAVE_GLIB2},0)
|
||||
BUILD_TARGETS := zgetdump
|
||||
INSTALL_TARGETS := install-zgetdump
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
OBJECTS = zgetdump.o opts.o zg.o zg_error.o zg_print.o \
|
||||
dfi.o dfi_mem_chunk.o dfi_vmcoreinfo.o \
|
||||
dfi_lkcd.o dfi_elf.o \
|
||||
dfi_lkcd.o dfi_elf.o dfi_elf_common.o dfi_pv_elf.o \
|
||||
dfi_s390.o dfi_s390_ext.o\
|
||||
dfi_s390mv.o dfi_s390mv_ext.o \
|
||||
dfi_s390tape.o dfi_kdump.o \
|
||||
@@ -54,7 +75,27 @@ OBJECTS = zgetdump.o opts.o zg.o zg_error.o zg_print.o \
|
||||
dt.o dt_s390sv.o dt_s390sv_ext.o \
|
||||
dt_s390mv.o dt_s390mv_ext.o \
|
||||
dt_scsi.o output.o \
|
||||
ngdump.o dt_ngdump.o dfi_ngdump.o
|
||||
ngdump.o dt_ngdump.o dfi_ngdump.o \
|
||||
pv_utils.o
|
||||
|
||||
ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
GLIB2_CFLAGS := $(shell pkg-config --silence-errors --cflags glib-2.0)
|
||||
GLIB2_LIBS := $(shell pkg-config --silence-errors --libs glib-2.0)
|
||||
LIBCRYPTO_CFLAGS := $(shell pkg-config --silence-errors --cflags libcrypto)
|
||||
LIBCRYPTO_LIBS := $(shell pkg-config --silence-errors --libs libcrypto)
|
||||
else
|
||||
GLIB2_CFLAGS := -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include
|
||||
GLIB2_LIBS := -lglib-2.0
|
||||
LIBCRYPTO_CFLAGS :=
|
||||
LIBCRYPTO_LIBS := -lcrypto
|
||||
endif
|
||||
|
||||
LIBPV = $(rootdir)/libpv/libpv.a
|
||||
LIBPV_CFLAGS := -DOPENSSL_API_COMPAT=0x10100000L \
|
||||
$(GLIB2_CFLAGS) \
|
||||
$(LIBCRYPTO_CFLAGS) \
|
||||
$(NULL)
|
||||
LIBPV_LIBS := $(GLIB2_LIBS) $(LIBCRYPTO_LIBS)
|
||||
|
||||
ifeq ("$(HAVE_FUSE)","0")
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=0 -D_FILE_OFFSET_BITS=64
|
||||
@@ -66,23 +107,30 @@ else
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse3
|
||||
FUSE_LDLIBS = -lfuse3
|
||||
endif
|
||||
LDLIBS += -lz $(FUSE_LDLIBS)
|
||||
ALL_CFLAGS += $(FUSE_CFLAGS)
|
||||
LDLIBS += -lz $(FUSE_LDLIBS) $(LIBPV_LIBS)
|
||||
ALL_CFLAGS += $(FUSE_CFLAGS) $(LIBPV_CFLAGS)
|
||||
|
||||
ifneq ("$(HAVE_FUSE)","0")
|
||||
OBJECTS += zfuse.o
|
||||
endif
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
zgetdump: $(OBJECTS) $(libs)
|
||||
libs = $(rootdir)/libutil/libutil.a $(LIBPV)
|
||||
|
||||
install: all
|
||||
all: $(BUILD_TARGETS)
|
||||
|
||||
zgetdump: .check_dep_zgetdump $(OBJECTS) $(libs)
|
||||
|
||||
skip-zgetdump:
|
||||
echo " SKIP zgetdump due to unresolved dependencies"
|
||||
|
||||
install-zgetdump: zgetdump
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -m 755 zgetdump $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -m 644 zgetdump.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ zgetdump core.*
|
||||
endif
|
||||
install: $(INSTALL_TARGETS)
|
||||
|
||||
.PHONY: all install clean check_dep_fuse check_dep_zlib
|
||||
clean:
|
||||
rm -f -- *.o *~ zgetdump core.* .detect_openssl.dep.c .check_dep_zgetdump
|
||||
|
||||
.PHONY: all install clean check_dep_fuse check_dep_zlib skip-zgetdump install-zgetdump
|
||||
|
||||
260
zdump/df_elf.c
260
zdump/df_elf.c
@@ -7,27 +7,23 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_log.h"
|
||||
|
||||
#include "df_elf.h"
|
||||
|
||||
#define NOTE_NAME_CORE "CORE"
|
||||
#define NOTE_NAME_LINUX "LINUX"
|
||||
#define NOTE_NAME_VMCOREINFO "VMCOREINFO"
|
||||
|
||||
/*
|
||||
* Initialize ELF header
|
||||
*/
|
||||
void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum)
|
||||
{
|
||||
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
|
||||
ehdr->e_ident[EI_CLASS] = ELFCLASS64;
|
||||
ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
|
||||
ehdr->e_ident[EI_VERSION] = EV_CURRENT;
|
||||
ehdr->e_ident[EI_VERSION] = ELF_VERSION_1;
|
||||
ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
|
||||
ehdr->e_ident[EI_ABIVERSION] = 0;
|
||||
memset(ehdr->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
|
||||
ehdr->e_type = ET_CORE;
|
||||
ehdr->e_machine = EM_S390;
|
||||
ehdr->e_version = EV_CURRENT;
|
||||
ehdr->e_version = ELF_VERSION_1;
|
||||
ehdr->e_entry = 0;
|
||||
ehdr->e_phoff = sizeof(Elf64_Ehdr);
|
||||
ehdr->e_shoff = 0;
|
||||
@@ -41,9 +37,188 @@ void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum)
|
||||
return ehdr + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ELF note
|
||||
*/
|
||||
bool ehdr_is_elf_object(const Elf64_Ehdr *ehdr)
|
||||
{
|
||||
return (ehdr->e_version == ELF_VERSION_1) && (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0);
|
||||
}
|
||||
|
||||
bool ehdr_is_vmcore(const Elf64_Ehdr *ehdr)
|
||||
{
|
||||
return ehdr->e_type == ET_CORE;
|
||||
}
|
||||
|
||||
bool ehdr_is_s390x(const Elf64_Ehdr *ehdr)
|
||||
{
|
||||
return ehdr->e_machine == EM_S390 && ehdr->e_ident[EI_DATA] == ELFDATA2MSB &&
|
||||
ehdr->e_ident[EI_CLASS] == ELFCLASS64;
|
||||
}
|
||||
|
||||
int ehdr_check_s390x(const Elf64_Ehdr *ehdr)
|
||||
{
|
||||
if (!ehdr_is_elf_object(ehdr) || !ehdr_is_vmcore(ehdr))
|
||||
return -ENODEV;
|
||||
if (!ehdr_is_s390x(ehdr))
|
||||
ERR_EXIT("Only s390x (64 bit) core dump files are supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Elf64_Ehdr *read_elf_hdr(const struct zg_fh *fh)
|
||||
{
|
||||
Elf64_Ehdr *ehdr;
|
||||
const size_t ehdr_size = sizeof(*ehdr);
|
||||
|
||||
if (zg_size(fh) < ehdr_size)
|
||||
return NULL;
|
||||
|
||||
ehdr = util_malloc(ehdr_size);
|
||||
zg_read(fh, ehdr, ehdr_size, ZG_CHECK);
|
||||
return ehdr;
|
||||
}
|
||||
|
||||
Elf64_Phdr *read_elf_phdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, unsigned int *phdr_count)
|
||||
{
|
||||
const Elf64_Half phnum = ehdr->e_phnum;
|
||||
size_t phdrs_size;
|
||||
Elf64_Phdr *phdrs;
|
||||
|
||||
if (!phnum) {
|
||||
*phdr_count = phnum;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ehdr->e_phoff > OFF_T_MAX)
|
||||
ERR_EXIT("Unsupported offset");
|
||||
|
||||
/* Cannot wraparound since `Elf64_Half`` is `uint16_t` */
|
||||
phdrs_size = sizeof(*phdrs) * phnum;
|
||||
phdrs = util_malloc(phdrs_size);
|
||||
zg_seek(fh, (off_t)ehdr->e_phoff, ZG_CHECK);
|
||||
zg_read(fh, phdrs, phdrs_size, ZG_CHECK);
|
||||
*phdr_count = phnum;
|
||||
return phdrs;
|
||||
}
|
||||
|
||||
Elf64_Shdr *read_elf_shdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, unsigned int *shdr_count)
|
||||
{
|
||||
const Elf64_Half shnum = ehdr->e_shnum;
|
||||
size_t shdrs_size;
|
||||
Elf64_Shdr *shdrs;
|
||||
|
||||
if (!shnum) {
|
||||
*shdr_count = shnum;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ehdr->e_shoff > OFF_T_MAX)
|
||||
ERR_EXIT("Unsupported offset");
|
||||
|
||||
/* Cannot wraparound since `Elf64_Half`` is `uint16_t` */
|
||||
shdrs_size = sizeof(*shdrs) * shnum;
|
||||
shdrs = util_malloc(shdrs_size);
|
||||
zg_seek(fh, (off_t)ehdr->e_shoff, ZG_CHECK);
|
||||
zg_read(fh, shdrs, shdrs_size, ZG_CHECK);
|
||||
*shdr_count = shnum;
|
||||
return shdrs;
|
||||
}
|
||||
|
||||
unsigned char *read_elf_section_data(const struct zg_fh *fh, const Elf64_Shdr *shdr, size_t *size)
|
||||
{
|
||||
const size_t sh_size = shdr->sh_size;
|
||||
unsigned char *ret;
|
||||
|
||||
if (!sh_size) {
|
||||
*size = sh_size;
|
||||
return NULL;
|
||||
}
|
||||
if (shdr->sh_offset > OFF_T_MAX)
|
||||
ERR_EXIT("Unsupported offset");
|
||||
|
||||
ret = util_malloc(sh_size);
|
||||
zg_seek(fh, (off_t)shdr->sh_offset, ZG_CHECK);
|
||||
zg_read(fh, ret, sh_size, ZG_CHECK);
|
||||
*size = sh_size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *read_elf_shstrtab(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, const Elf64_Shdr *shdrs,
|
||||
const unsigned int shnum, size_t *shstrtab_size)
|
||||
{
|
||||
const size_t shstrndx = ehdr->e_shstrndx;
|
||||
Elf64_Xword tmp_shstrtab_size;
|
||||
Elf64_Off shstrndx_off;
|
||||
char *shstrtab;
|
||||
|
||||
/* file has no section name string table */
|
||||
if (shstrndx == SHN_UNDEF) {
|
||||
*shstrtab_size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Invalid ELF file */
|
||||
if (shstrndx >= shnum) {
|
||||
util_log_print(UTIL_LOG_ERROR, "Invalid ELF file: invalid string table index\n");
|
||||
*shstrtab_size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
shstrndx_off = shdrs[shstrndx].sh_offset;
|
||||
if (shstrndx_off > OFF_T_MAX)
|
||||
ERR_EXIT("Unsupported offset");
|
||||
|
||||
tmp_shstrtab_size = shdrs[shstrndx].sh_size;
|
||||
shstrtab = util_malloc(tmp_shstrtab_size);
|
||||
zg_seek(fh, (off_t)shstrndx_off, ZG_CHECK);
|
||||
zg_read(fh, shstrtab, tmp_shstrtab_size, ZG_CHECK);
|
||||
*shstrtab_size = tmp_shstrtab_size;
|
||||
return shstrtab;
|
||||
}
|
||||
|
||||
const Elf64_Shdr *find_elf_shdr_by_name(const Elf64_Shdr *shdrs, const unsigned int shnum,
|
||||
const char *const shstrtab, const size_t shstrtab_size,
|
||||
const char *name)
|
||||
{
|
||||
const Elf64_Shdr *shdr = shdrs;
|
||||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < shnum; idx++, shdr++) {
|
||||
/* Invalid ELF file */
|
||||
if (shdr->sh_name >= shstrtab_size) {
|
||||
util_log_print(UTIL_LOG_ERROR,
|
||||
"Invalid ELF file: section name is too large\n");
|
||||
return NULL;
|
||||
}
|
||||
if (strcmp(name, &shstrtab[shdr->sh_name]) == 0)
|
||||
return shdr;
|
||||
}
|
||||
/* Not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int nt_read(const struct zg_fh *fh, const Elf64_Nhdr *note, void *buf, size_t buf_len)
|
||||
{
|
||||
ssize_t nread;
|
||||
|
||||
/* We cannot read more than the current note provides */
|
||||
if (note->n_descsz < buf_len)
|
||||
return -EINVAL;
|
||||
/* Skip note's name and position file at note's descriptor */
|
||||
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_namesz), ZG_CHECK);
|
||||
/* Read note's descriptor */
|
||||
nread = zg_read(fh, buf, buf_len, ZG_CHECK_ERR);
|
||||
if (nread < 0 || (size_t)nread != buf_len)
|
||||
return -EINVAL;
|
||||
/* Skip the rest of note's descriptor until the next note */
|
||||
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_descsz) - buf_len, ZG_CHECK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nt_skip(const struct zg_fh *fh, const Elf64_Nhdr *note)
|
||||
{
|
||||
/* Skip note's name + descriptor and position file at the next note */
|
||||
zg_seek_cur(fh, ELF_NOTE_ROUNDUP(note->n_namesz) + ELF_NOTE_ROUNDUP(note->n_descsz),
|
||||
ZG_CHECK);
|
||||
}
|
||||
|
||||
void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
|
||||
const char *name)
|
||||
{
|
||||
@@ -57,17 +232,14 @@ void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
|
||||
len = sizeof(Elf64_Nhdr);
|
||||
|
||||
memcpy(buf + len, name, note->n_namesz);
|
||||
len = ROUNDUP(len + note->n_namesz, 4);
|
||||
len = ELF_NOTE_ROUNDUP(len + note->n_namesz);
|
||||
|
||||
memcpy(buf + len, desc, note->n_descsz);
|
||||
len = ROUNDUP(len + note->n_descsz, 4);
|
||||
len = ELF_NOTE_ROUNDUP(len + note->n_descsz);
|
||||
|
||||
return PTR_ADD(buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prstatus note
|
||||
*/
|
||||
void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
struct nt_prstatus_64 nt_prstatus;
|
||||
@@ -84,9 +256,6 @@ void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu)
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize fpregset (floating point) note
|
||||
*/
|
||||
void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
struct nt_fpregset_64 nt_fpregset;
|
||||
@@ -99,72 +268,53 @@ void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu)
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize timer note
|
||||
*/
|
||||
void *nt_s390_timer(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TIMER, &cpu->timer, sizeof(cpu->timer),
|
||||
NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize TOD clock comparator note
|
||||
*/
|
||||
void *nt_s390_tod_cmp(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TODCMP, &cpu->todcmp,
|
||||
sizeof(cpu->todcmp), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize TOD programmable register note
|
||||
*/
|
||||
void *nt_s390_tod_preg(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_TODPREG, &cpu->todpreg,
|
||||
sizeof(cpu->todpreg), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize control register note
|
||||
*/
|
||||
void *nt_s390_ctrs(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_CTRS, &cpu->ctrs, sizeof(cpu->ctrs),
|
||||
NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prefix register note
|
||||
*/
|
||||
void *nt_s390_prefix(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_PREFIX, &cpu->prefix,
|
||||
sizeof(cpu->prefix), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vxrs_low register note
|
||||
*/
|
||||
void *nt_s390_vxrs_low(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_LOW, &cpu->vxrs_low,
|
||||
sizeof(cpu->vxrs_low), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vxrs_high register note
|
||||
*/
|
||||
void *nt_s390_vxrs_high(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_HIGH, &cpu->vxrs_high,
|
||||
sizeof(cpu->vxrs_high), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize prpsinfo note
|
||||
*/
|
||||
void *nt_s390_gs_cb(void *ptr, const struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_GS_CB, &cpu->gscb, sizeof(cpu->gscb), NOTE_NAME_LINUX);
|
||||
}
|
||||
|
||||
void *nt_prpsinfo(void *ptr)
|
||||
{
|
||||
struct nt_prpsinfo_64 prpsinfo;
|
||||
@@ -179,9 +329,6 @@ void *nt_prpsinfo(void *ptr)
|
||||
NOTE_NAME_CORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vmcoreinfo note
|
||||
*/
|
||||
void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo)
|
||||
{
|
||||
if (!vmcoreinfo)
|
||||
@@ -189,3 +336,22 @@ void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo)
|
||||
return nt_init(ptr, 0, vmcoreinfo, strlen(vmcoreinfo),
|
||||
NOTE_NAME_VMCOREINFO);
|
||||
}
|
||||
|
||||
/* Keep in sync with `struct dfi_cpu` */
|
||||
size_t get_max_note_size_per_cpu(void)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_CORE, sizeof(struct nt_prstatus_64));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_CORE, sizeof(struct nt_fpregset_64));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, timer));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, todcmp));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, todpreg));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, ctrs));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, prefix));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, vxrs_low));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, vxrs_high));
|
||||
size += ELF64_NOTE_SIZE(NOTE_NAME_LINUX, sizeof_field(struct dfi_cpu, gscb));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
158
zdump/df_elf.h
158
zdump/df_elf.h
@@ -14,12 +14,17 @@
|
||||
|
||||
#include <elf.h>
|
||||
#include <linux/types.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "zg.h"
|
||||
#include "dfi.h"
|
||||
|
||||
#ifndef ELF_VERSION_1
|
||||
#define ELF_VERSION_1 0x1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* S390 CPU timer note (u64)
|
||||
*/
|
||||
@@ -69,6 +74,29 @@
|
||||
#define NT_S390_VXRS_HIGH 0x30a
|
||||
#endif
|
||||
|
||||
/*
|
||||
* S390 guarded-storage registers note (4 * u64)
|
||||
*/
|
||||
#ifndef NT_S390_GS_CB
|
||||
#define NT_S390_GS_CB 0x30b
|
||||
#endif
|
||||
|
||||
/*
|
||||
* S390 confidential CPU data from secure guest.
|
||||
*/
|
||||
#ifndef NT_S390_PV_CPU_DATA
|
||||
#define NT_S390_PV_CPU_DATA 0x30e
|
||||
#endif
|
||||
|
||||
#define NOTE_NAME_CORE "CORE"
|
||||
#define NOTE_NAME_LINUX "LINUX"
|
||||
#define NOTE_NAME_VMCOREINFO "VMCOREINFO"
|
||||
|
||||
#define ELF_NOTE_ROUNDUP(size) ROUNDUP(size, 4)
|
||||
#define ELF_NOTE_NAME_SIZE(name) ELF_NOTE_ROUNDUP(name ? strlen(name) + 1 : 0)
|
||||
#define ELF64_NOTE_SIZE(name, desc_size) \
|
||||
(sizeof(Elf64_Nhdr) + ELF_NOTE_NAME_SIZE(name) + ELF_NOTE_ROUNDUP(desc_size))
|
||||
|
||||
/*
|
||||
* prstatus ELF Note
|
||||
*/
|
||||
@@ -116,20 +144,150 @@ static inline void df_elf_ensure_s390x(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ELF header
|
||||
*/
|
||||
void *ehdr_init(Elf64_Ehdr *ehdr, Elf64_Half phnum);
|
||||
|
||||
/*
|
||||
* Checks whether @ehdr is a ELF object
|
||||
*/
|
||||
bool ehdr_is_elf_object(const Elf64_Ehdr *ehdr);
|
||||
|
||||
/*
|
||||
* Checks whether @ehdr is a vmcore
|
||||
*/
|
||||
bool ehdr_is_vmcore(const Elf64_Ehdr *ehdr);
|
||||
|
||||
/*
|
||||
* Checks whether @ehdr is a s390x header
|
||||
*/
|
||||
bool ehdr_is_s390x(const Elf64_Ehdr *ehdr);
|
||||
|
||||
/*
|
||||
* Verify that given header is a s390x ELF header
|
||||
*/
|
||||
int ehdr_check_s390x(const Elf64_Ehdr *ehdr);
|
||||
|
||||
/*
|
||||
* Read ELF header at current offset
|
||||
*/
|
||||
Elf64_Ehdr *read_elf_hdr(const struct zg_fh *fh);
|
||||
|
||||
/*
|
||||
* Read ELF program headers
|
||||
*
|
||||
* To read the program headers the offset of @fh is changed.
|
||||
*/
|
||||
Elf64_Phdr *read_elf_phdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr,
|
||||
unsigned int *phdr_count);
|
||||
|
||||
/*
|
||||
* Read ELF section headers
|
||||
*
|
||||
* To read the section headers the offset of @fh is changed.
|
||||
*/
|
||||
Elf64_Shdr *read_elf_shdrs(const struct zg_fh *fh, const Elf64_Ehdr *ehdr,
|
||||
unsigned int *shdr_count);
|
||||
|
||||
/*
|
||||
* Read ELF section content
|
||||
*
|
||||
* To read the section content the offset of @fh is changed.
|
||||
*/
|
||||
unsigned char *read_elf_section_data(const struct zg_fh *fh, const Elf64_Shdr *shdr, size_t *size);
|
||||
|
||||
/*
|
||||
* Read ELF section header string table
|
||||
*
|
||||
* To read the section header string table the offset of @fh is changed.
|
||||
*/
|
||||
char *read_elf_shstrtab(const struct zg_fh *fh, const Elf64_Ehdr *ehdr, const Elf64_Shdr *shdrs,
|
||||
const unsigned int shnum, size_t *shstrtab_size);
|
||||
|
||||
/*
|
||||
* Find ELF section header by section name
|
||||
*/
|
||||
const Elf64_Shdr *find_elf_shdr_by_name(const Elf64_Shdr *shdrs, const unsigned int shnum,
|
||||
const char *shstrtab, const size_t shstrtab_size,
|
||||
const char *name);
|
||||
|
||||
/*
|
||||
* Read note and store the note description in @buf
|
||||
*/
|
||||
int nt_read(const struct zg_fh *fh, const Elf64_Nhdr *note, void *buf, size_t buf_len);
|
||||
|
||||
/*
|
||||
* Skip note
|
||||
*/
|
||||
void nt_skip(const struct zg_fh *fh, const Elf64_Nhdr *note);
|
||||
|
||||
/*
|
||||
* Initialize ELF note
|
||||
*/
|
||||
void *nt_init(void *buf, Elf64_Word type, const void *desc, int d_len,
|
||||
const char *name);
|
||||
|
||||
/*
|
||||
* Initialize prstatus note
|
||||
*/
|
||||
void *nt_prstatus(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize fpregset (floating point) note
|
||||
*/
|
||||
void *nt_fpregset(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize timer note
|
||||
*/
|
||||
void *nt_s390_timer(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize TOD clock comparator note
|
||||
*/
|
||||
void *nt_s390_tod_cmp(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize TOD programmable register note
|
||||
*/
|
||||
void *nt_s390_tod_preg(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize control register note
|
||||
*/
|
||||
void *nt_s390_ctrs(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize prefix register note
|
||||
*/
|
||||
void *nt_s390_prefix(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize vxrs_low register note
|
||||
*/
|
||||
void *nt_s390_vxrs_low(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize vxrs_high register note
|
||||
*/
|
||||
void *nt_s390_vxrs_high(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize guarded-storage registers note
|
||||
*/
|
||||
void *nt_s390_gs_cb(void *ptr, const struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* Initialize prpsinfo note
|
||||
*/
|
||||
void *nt_prpsinfo(void *ptr);
|
||||
|
||||
/*
|
||||
* Initialize vmcoreinfo note
|
||||
*/
|
||||
void *nt_vmcoreinfo(void *ptr, const char *vmcoreinfo);
|
||||
|
||||
size_t get_max_note_size_per_cpu(void);
|
||||
|
||||
#endif /* DF_ELF_H */
|
||||
|
||||
10
zdump/dfi.c
10
zdump/dfi.c
@@ -26,6 +26,7 @@
|
||||
* DFI vector - ensure that tape is the first in the list and devmem the second!
|
||||
*/
|
||||
static struct dfi *dfi_vec[] = {
|
||||
/* clang-format off */
|
||||
&dfi_s390tape,
|
||||
&dfi_devmem,
|
||||
&dfi_s390mv_ext,
|
||||
@@ -33,11 +34,13 @@ static struct dfi *dfi_vec[] = {
|
||||
&dfi_s390_ext,
|
||||
&dfi_s390,
|
||||
&dfi_lkcd,
|
||||
&dfi_pv_elf,
|
||||
&dfi_elf,
|
||||
&dfi_kdump,
|
||||
&dfi_kdump_flat,
|
||||
&dfi_ngdump,
|
||||
NULL,
|
||||
/* clang-format on */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -168,6 +171,11 @@ struct dfi_cpu *dfi_cpu_alloc(void)
|
||||
return zg_alloc(sizeof(struct dfi_cpu));
|
||||
}
|
||||
|
||||
void dfi_cpu_free(struct dfi_cpu *cpu)
|
||||
{
|
||||
zg_free(cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add DFI CPU
|
||||
*/
|
||||
@@ -780,7 +788,7 @@ int dfi_init(void)
|
||||
}
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI %s returned with rc %d\n",
|
||||
dfi->name, rc);
|
||||
if (rc == 0 || rc == -EINVAL)
|
||||
if (rc == 0 || rc == -EINVAL || rc == -ENOKEY)
|
||||
return rc;
|
||||
zg_close(g.fh);
|
||||
i++;
|
||||
|
||||
14
zdump/dfi.h
14
zdump/dfi.h
@@ -99,6 +99,15 @@ struct dfi_cpu {
|
||||
u32 todpreg;
|
||||
u64 vxrs_low[16];
|
||||
struct dfi_vxrs vxrs_high[16];
|
||||
union {
|
||||
u64 gscb[4];
|
||||
struct {
|
||||
u64 reserved;
|
||||
u64 gsd;
|
||||
u64 gssm;
|
||||
u64 gs_epl_a;
|
||||
};
|
||||
};
|
||||
u16 cpu_id;
|
||||
};
|
||||
|
||||
@@ -127,7 +136,8 @@ enum dfi_cpu_content {
|
||||
DFI_CPU_CONTENT_ALL, /* Complete register information available */
|
||||
};
|
||||
|
||||
#define DFI_CPU_CONTENT_FAC_VX 0x00000001
|
||||
#define DFI_CPU_CONTENT_FAC_VX 0x00000001
|
||||
#define DFI_CPU_CONTENT_FAC_GS 0x00000002
|
||||
int dfi_cpu_content_fac_check(int flags);
|
||||
void dfi_cpu_content_fac_add(int flags);
|
||||
|
||||
@@ -137,6 +147,7 @@ void dfi_cpu_content_fac_add(int flags);
|
||||
struct util_list *dfi_cpu_list(void);
|
||||
void dfi_cpu_info_init(enum dfi_cpu_content content);
|
||||
struct dfi_cpu *dfi_cpu_alloc(void);
|
||||
void dfi_cpu_free(struct dfi_cpu *cpu);
|
||||
struct dfi_cpu *dfi_cpu(unsigned int cpu_nr);
|
||||
void dfi_cpu_add(struct dfi_cpu *cpu);
|
||||
unsigned int dfi_cpu_cnt(void);
|
||||
@@ -238,6 +249,7 @@ extern struct dfi dfi_s390mv_ext;
|
||||
extern struct dfi dfi_s390;
|
||||
extern struct dfi dfi_s390_ext;
|
||||
extern struct dfi dfi_lkcd;
|
||||
extern struct dfi dfi_pv_elf;
|
||||
extern struct dfi dfi_elf;
|
||||
extern struct dfi dfi_kdump;
|
||||
extern struct dfi dfi_kdump_flat;
|
||||
|
||||
235
zdump/dfi_elf.c
235
zdump/dfi_elf.c
@@ -24,6 +24,7 @@
|
||||
#include "df_elf.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfi_elf_common.h"
|
||||
|
||||
/*
|
||||
* Read memory for given memory chunk
|
||||
@@ -37,77 +38,6 @@ static void dfi_elf_mem_chunk_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off,
|
||||
zg_read(g.fh, buf, cnt, ZG_CHECK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add load (memory chunk) to DFI dump
|
||||
*/
|
||||
static int pt_load_add(Elf64_Phdr *phdr)
|
||||
{
|
||||
u64 *off_ptr;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG,
|
||||
"DFI ELF p_paddr 0x%016lx p_vaddr 0x%016lx p_offset 0x%016lx p_filesz 0x%016lx p_memsz 0x%016lx\n",
|
||||
phdr->p_paddr, phdr->p_vaddr, phdr->p_offset,
|
||||
phdr->p_filesz, phdr->p_memsz);
|
||||
|
||||
if (phdr->p_paddr != phdr->p_vaddr) {
|
||||
phdr->p_paddr = phdr->p_vaddr;
|
||||
STDERR("Dump file \"%s\" is a user space core dump\n",
|
||||
g.opts.device);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (phdr->p_memsz == 0)
|
||||
return -EINVAL;
|
||||
if (phdr->p_offset + phdr->p_filesz > zg_size(g.fh))
|
||||
return -EINVAL;
|
||||
if (phdr->p_filesz > phdr->p_memsz)
|
||||
return -EINVAL;
|
||||
if (phdr->p_filesz > 0) {
|
||||
off_ptr = zg_alloc(sizeof(*off_ptr));
|
||||
*off_ptr = phdr->p_offset;
|
||||
dfi_mem_chunk_add(phdr->p_paddr, phdr->p_filesz, off_ptr,
|
||||
dfi_elf_mem_chunk_read_fn, zg_free);
|
||||
}
|
||||
if (phdr->p_memsz - phdr->p_filesz > 0) {
|
||||
/* Add zero memory chunk */
|
||||
dfi_mem_chunk_add(phdr->p_paddr + phdr->p_filesz,
|
||||
phdr->p_memsz - phdr->p_filesz, NULL,
|
||||
dfi_mem_chunk_read_zero, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read note
|
||||
*/
|
||||
static int nt_read(const Elf64_Nhdr *note, void *buf, size_t buf_len)
|
||||
{
|
||||
ssize_t nread;
|
||||
|
||||
/* We cannot read more than the current note provides */
|
||||
if (note->n_descsz < buf_len)
|
||||
return -EINVAL;
|
||||
/* Skip note's name and position file at note's descriptor */
|
||||
zg_seek_cur(g.fh, ROUNDUP(note->n_namesz, 4), ZG_CHECK);
|
||||
/* Read note's descriptor */
|
||||
nread = zg_read(g.fh, buf, buf_len, ZG_CHECK_ERR);
|
||||
if (nread < 0 || (size_t)nread != buf_len)
|
||||
return -EINVAL;
|
||||
/* Skip the rest of note's descriptor until the next note */
|
||||
zg_seek_cur(g.fh, ROUNDUP(note->n_descsz, 4) - buf_len, ZG_CHECK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip note
|
||||
*/
|
||||
static void nt_skip(const Elf64_Nhdr *note)
|
||||
{
|
||||
/* Skip note's name + descriptor and position file at the next note */
|
||||
zg_seek_cur(g.fh,
|
||||
ROUNDUP(note->n_namesz, 4) + ROUNDUP(note->n_descsz, 4),
|
||||
ZG_CHECK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that CPU is already defined by prstatus note
|
||||
*/
|
||||
@@ -121,12 +51,12 @@ static void check_cpu(struct dfi_cpu *cpu, const char *note_str)
|
||||
/*
|
||||
* Read prstatus note and return new DFI CPU
|
||||
*/
|
||||
static struct dfi_cpu *nt_prstatus_read(Elf64_Nhdr *note)
|
||||
static struct dfi_cpu *nt_prstatus_read(const struct zg_fh *fh, const Elf64_Nhdr *note)
|
||||
{
|
||||
struct dfi_cpu *cpu = dfi_cpu_alloc();
|
||||
struct nt_prstatus_64 nt_prstatus;
|
||||
|
||||
if (nt_read(note, &nt_prstatus, sizeof(nt_prstatus)))
|
||||
if (nt_read(fh, note, &nt_prstatus, sizeof(nt_prstatus)))
|
||||
return NULL;
|
||||
|
||||
memcpy(cpu->gprs, &nt_prstatus.gprs, sizeof(cpu->gprs));
|
||||
@@ -140,12 +70,12 @@ static struct dfi_cpu *nt_prstatus_read(Elf64_Nhdr *note)
|
||||
/*
|
||||
* Read fpregset note
|
||||
*/
|
||||
static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_fpregset_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
struct nt_fpregset_64 nt_fpregset;
|
||||
|
||||
check_cpu(cpu, "FPREGSET");
|
||||
if (nt_read(note, &nt_fpregset, sizeof(nt_fpregset)))
|
||||
if (nt_read(fh, note, &nt_fpregset, sizeof(nt_fpregset)))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&cpu->fpc, &nt_fpregset.fpc, sizeof(cpu->fpc));
|
||||
@@ -156,195 +86,208 @@ static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
/*
|
||||
* Read s390 timer note
|
||||
*/
|
||||
static int nt_s390_timer_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_timer_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_TIMER");
|
||||
return nt_read(note, &cpu->timer, sizeof(cpu->timer));
|
||||
return nt_read(fh, note, &cpu->timer, sizeof(cpu->timer));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 todcmp note
|
||||
*/
|
||||
static int nt_s390_todcmp_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_todcmp_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_TODCMP");
|
||||
return nt_read(note, &cpu->todcmp, sizeof(cpu->todcmp));
|
||||
return nt_read(fh, note, &cpu->todcmp, sizeof(cpu->todcmp));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 todpreg note
|
||||
*/
|
||||
static int nt_s390_todpreg_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_todpreg_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_TODPREG");
|
||||
return nt_read(note, &cpu->todpreg, sizeof(cpu->todpreg));
|
||||
return nt_read(fh, note, &cpu->todpreg, sizeof(cpu->todpreg));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 ctrs note
|
||||
*/
|
||||
static int nt_s390_ctrs_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_ctrs_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_CTRS");
|
||||
return nt_read(note, &cpu->ctrs, sizeof(cpu->ctrs));
|
||||
return nt_read(fh, note, &cpu->ctrs, sizeof(cpu->ctrs));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 prefix note
|
||||
*/
|
||||
static int nt_s390_prefix_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_prefix_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_PREFIX");
|
||||
return nt_read(note, &cpu->prefix, sizeof(cpu->prefix));
|
||||
return nt_read(fh, note, &cpu->prefix, sizeof(cpu->prefix));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 vxrs_low note
|
||||
*/
|
||||
static int nt_s390_vxrs_low_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_vxrs_low_read(const struct zg_fh *fh, struct dfi_cpu *cpu,
|
||||
const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_VXRS_LOW");
|
||||
return nt_read(note, &cpu->vxrs_low, sizeof(cpu->vxrs_low));
|
||||
return nt_read(fh, note, &cpu->vxrs_low, sizeof(cpu->vxrs_low));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 vxrs_high note
|
||||
*/
|
||||
static int nt_s390_vxrs_high_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
static int nt_s390_vxrs_high_read(const struct zg_fh *fh, struct dfi_cpu *cpu,
|
||||
const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_VXRS_HIGH");
|
||||
return nt_read(note, &cpu->vxrs_high, sizeof(cpu->vxrs_high));
|
||||
return nt_read(fh, note, &cpu->vxrs_high, sizeof(cpu->vxrs_high));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 gs_cb note
|
||||
*/
|
||||
static int nt_s390_gs_cb_read(const struct zg_fh *fh, struct dfi_cpu *cpu, const Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_GSCB");
|
||||
return nt_read(fh, note, &cpu->gscb, sizeof(cpu->gscb));
|
||||
}
|
||||
|
||||
/*
|
||||
* Add all notes for notes phdr
|
||||
*/
|
||||
static int pt_notes_add(Elf64_Phdr *phdr)
|
||||
static int pt_notes_add(const Elf64_Phdr *phdr)
|
||||
{
|
||||
struct dfi_cpu *cpu_current = NULL;
|
||||
u64 notes_start_off;
|
||||
Elf64_Nhdr note;
|
||||
const struct zg_fh *fh = g.fh;
|
||||
int rc;
|
||||
|
||||
zg_seek(g.fh, phdr->p_offset, ZG_CHECK);
|
||||
notes_start_off = zg_tell(g.fh, ZG_CHECK);
|
||||
while (zg_tell(g.fh, ZG_CHECK) - notes_start_off < phdr->p_filesz) {
|
||||
rc = zg_read(g.fh, ¬e, sizeof(note), ZG_CHECK_ERR);
|
||||
zg_seek(fh, phdr->p_offset, ZG_CHECK);
|
||||
while (zg_tell(fh, ZG_CHECK) - phdr->p_offset < phdr->p_filesz) {
|
||||
Elf64_Nhdr note;
|
||||
|
||||
rc = zg_read(fh, ¬e, sizeof(note), ZG_CHECK_ERR);
|
||||
if (rc != sizeof(note))
|
||||
return -EINVAL;
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF n_type 0x%x\n",
|
||||
note.n_type);
|
||||
switch (note.n_type) {
|
||||
case NT_PRSTATUS:
|
||||
cpu_current = nt_prstatus_read(¬e);
|
||||
cpu_current = nt_prstatus_read(fh, ¬e);
|
||||
if (!cpu_current)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_FPREGSET:
|
||||
if (nt_fpregset_read(cpu_current, ¬e))
|
||||
if (nt_fpregset_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_TIMER:
|
||||
if (nt_s390_timer_read(cpu_current, ¬e))
|
||||
if (nt_s390_timer_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_TODCMP:
|
||||
if (nt_s390_todcmp_read(cpu_current, ¬e))
|
||||
if (nt_s390_todcmp_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_TODPREG:
|
||||
if (nt_s390_todpreg_read(cpu_current, ¬e))
|
||||
if (nt_s390_todpreg_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_CTRS:
|
||||
if (nt_s390_ctrs_read(cpu_current, ¬e))
|
||||
if (nt_s390_ctrs_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_PREFIX:
|
||||
if (nt_s390_prefix_read(cpu_current, ¬e))
|
||||
if (nt_s390_prefix_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_VXRS_LOW:
|
||||
if (nt_s390_vxrs_low_read(cpu_current, ¬e))
|
||||
if (nt_s390_vxrs_low_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
|
||||
break;
|
||||
case NT_S390_VXRS_HIGH:
|
||||
if (nt_s390_vxrs_high_read(cpu_current, ¬e))
|
||||
if (nt_s390_vxrs_high_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
|
||||
break;
|
||||
case NT_S390_GS_CB:
|
||||
if (nt_s390_gs_cb_read(fh, cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_GS);
|
||||
break;
|
||||
default:
|
||||
nt_skip(¬e);
|
||||
nt_skip(fh, ¬e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read ELF header
|
||||
*/
|
||||
static int read_elf_hdr(Elf64_Ehdr *ehdr)
|
||||
{
|
||||
if (zg_size(g.fh) < sizeof(*ehdr))
|
||||
return -ENODEV;
|
||||
zg_read(g.fh, ehdr, sizeof(*ehdr), ZG_CHECK);
|
||||
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
|
||||
return -ENODEV;
|
||||
if (ehdr->e_type != ET_CORE)
|
||||
return -ENODEV;
|
||||
if (ehdr->e_machine != EM_S390 || ehdr->e_ident[EI_CLASS] != ELFCLASS64)
|
||||
ERR_EXIT("Only s390x (64 bit) core dump files are supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ELF input dump format
|
||||
*/
|
||||
static int dfi_elf_init(void)
|
||||
{
|
||||
Elf64_Ehdr ehdr;
|
||||
Elf64_Phdr *phdr;
|
||||
int i;
|
||||
unsigned int phnum, i;
|
||||
Elf64_Phdr *phdrs;
|
||||
Elf64_Ehdr *ehdr;
|
||||
int rc = -ENODEV;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF initialization\n");
|
||||
|
||||
if (read_elf_hdr(&ehdr) != 0)
|
||||
ehdr = read_elf_hdr(g.fh);
|
||||
if (!ehdr)
|
||||
return -ENODEV;
|
||||
|
||||
if (ehdr_check_s390x(ehdr) < 0)
|
||||
goto free_ehdr;
|
||||
|
||||
df_elf_ensure_s390x();
|
||||
dfi_arch_set(DFI_ARCH_64);
|
||||
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
||||
|
||||
phdr = util_malloc(sizeof(*phdr) * ehdr.e_phnum);
|
||||
zg_seek(g.fh, ehdr.e_phoff, ZG_CHECK);
|
||||
zg_read(g.fh, phdr, sizeof(*phdr) * ehdr.e_phnum, ZG_CHECK);
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF e_phnum %u\n", ehdr.e_phnum);
|
||||
for (i = 0; i < ehdr.e_phnum; i++) {
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF p_type[%d] 0x%lx\n",
|
||||
i, phdr[i].p_type);
|
||||
switch (phdr[i].p_type) {
|
||||
case PT_LOAD:
|
||||
if (pt_load_add(&phdr[i])) {
|
||||
free(phdr);
|
||||
return -EINVAL;
|
||||
phdrs = read_elf_phdrs(g.fh, ehdr, &phnum);
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF e_phnum %u\n", phnum);
|
||||
for (i = 0; i < phnum; i++) {
|
||||
const Elf64_Phdr *phdr = &phdrs[i];
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, "DFI ELF p_type[%d] 0x%lx\n", i, phdr->p_type);
|
||||
switch (phdr->p_type) {
|
||||
case PT_LOAD: {
|
||||
u64 *off_ptr = zg_alloc(sizeof(*off_ptr));
|
||||
*off_ptr = phdr->p_offset;
|
||||
|
||||
if (pt_load_add(g.fh, phdr, (void **)&off_ptr, dfi_elf_mem_chunk_read_fn,
|
||||
free) < 0) {
|
||||
free(off_ptr);
|
||||
rc = -EINVAL;
|
||||
goto free_phdrs;
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
case PT_NOTE:
|
||||
if (pt_notes_add(&phdr[i])) {
|
||||
free(phdr);
|
||||
return -EINVAL;
|
||||
if (pt_notes_add(phdr)) {
|
||||
rc = -EINVAL;
|
||||
goto free_phdrs;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(phdr);
|
||||
|
||||
dfi_attr_version_set(ehdr.e_ident[EI_VERSION]);
|
||||
return 0;
|
||||
dfi_attr_version_set(ehdr->e_ident[EI_VERSION]);
|
||||
rc = 0;
|
||||
|
||||
free_phdrs:
|
||||
free(phdrs);
|
||||
free_ehdr:
|
||||
free(ehdr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
61
zdump/dfi_elf_common.c
Normal file
61
zdump/dfi_elf_common.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Common ELF core dump input format definitions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "dfi_elf_common.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
|
||||
int pt_load_add(const struct zg_fh *fh, const Elf64_Phdr *phdr, void **data,
|
||||
dfi_mem_chunk_read_fn read_fn, dfi_mem_chunk_free_fn free_fn)
|
||||
{
|
||||
assert(fh);
|
||||
assert(phdr);
|
||||
assert(data);
|
||||
assert(read_fn);
|
||||
assert(free_fn);
|
||||
|
||||
util_log_print(
|
||||
UTIL_LOG_DEBUG,
|
||||
"%s p_paddr 0x%016lx p_vaddr 0x%016lx p_offset 0x%016lx p_filesz 0x%016lx p_memsz 0x%016lx\n",
|
||||
__func__, phdr->p_paddr, phdr->p_vaddr, phdr->p_offset, phdr->p_filesz,
|
||||
phdr->p_memsz);
|
||||
|
||||
if (phdr->p_paddr != phdr->p_vaddr) {
|
||||
STDERR("Dump file \"%s\" is a user space core dump\n", fh->path);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (phdr->p_memsz == 0)
|
||||
return -EINVAL;
|
||||
if (phdr->p_offset + phdr->p_filesz > zg_size(fh))
|
||||
return -EINVAL;
|
||||
if (phdr->p_filesz > phdr->p_memsz)
|
||||
return -EINVAL;
|
||||
/* check for wrap-around */
|
||||
if (phdr->p_paddr + phdr->p_filesz < phdr->p_paddr)
|
||||
return -EINVAL;
|
||||
|
||||
if (phdr->p_filesz > 0) {
|
||||
dfi_mem_chunk_add(phdr->p_paddr, phdr->p_filesz, *data, read_fn, free_fn);
|
||||
*data = NULL;
|
||||
} else {
|
||||
/* Free @data directly as it's not used */
|
||||
free_fn(*data);
|
||||
*data = NULL;
|
||||
}
|
||||
if (phdr->p_memsz - phdr->p_filesz > 0) {
|
||||
/* Add zero memory chunk */
|
||||
dfi_mem_chunk_add(phdr->p_paddr + phdr->p_filesz, phdr->p_memsz - phdr->p_filesz,
|
||||
NULL, dfi_mem_chunk_read_zero, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
36
zdump/dfi_elf_common.h
Normal file
36
zdump/dfi_elf_common.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Common ELF core dump input format definitions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef DFI_ELF_COMMON_H
|
||||
#define DFI_ELF_COMMON_H
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "zg.h"
|
||||
|
||||
/**
|
||||
* pt_load_add:
|
||||
* @fh: (not nullable): open input file
|
||||
* @phdr: (not nullable): program ELF header of the load segment to add
|
||||
* @data: (not nullable): arbitrary pointer passed to the read callback @read_fn
|
||||
* @read_fn: (not nullable): callback used for reading the data of a memory chunk
|
||||
* @free_fn: (not nullable): function called to cleanup @data
|
||||
*
|
||||
* Add load (memory chunk) to DFI dump. After a successful call, @data belongs
|
||||
* to the mem_chunk structure created and the @data pointer is set to %NULL.
|
||||
*
|
||||
* Returns: %0 on success, returns < 0 in case of an error
|
||||
*/
|
||||
int pt_load_add(const struct zg_fh *fh, const Elf64_Phdr *phdr, void **data,
|
||||
dfi_mem_chunk_read_fn read_fn, dfi_mem_chunk_free_fn free_fn);
|
||||
|
||||
#endif /* DFI_ELF_COMMON_H */
|
||||
337
zdump/dfi_pv_elf.c
Normal file
337
zdump/dfi_pv_elf.c
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* PV ELF core dump input format
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <error.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
#include "libpv/glib-helper.h"
|
||||
|
||||
#include "pv_defs.h"
|
||||
#include "pv_utils.h"
|
||||
#include "zgetdump.h"
|
||||
#include "zg.h"
|
||||
#include "df_elf.h"
|
||||
#include "dfi.h"
|
||||
#include "dfi_mem_chunk.h"
|
||||
#include "dfi_elf_common.h"
|
||||
#include "opts.h"
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf64_Phdr, free);
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf64_Shdr, free);
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf64_Ehdr, free);
|
||||
|
||||
/**
|
||||
* read_elf_section_data_as_gbytes:
|
||||
* @fh: (not nullable): open input file
|
||||
* @shdr: (not nullable): section header of the section to read
|
||||
*
|
||||
* Try to read section data and return it as #GBytes.
|
||||
*
|
||||
* Returns: #GBytes on success, %NULL if an error occurred
|
||||
*/
|
||||
static GBytes *read_elf_section_data_as_gbytes(const struct zg_fh *fh, const Elf64_Shdr *shdr)
|
||||
{
|
||||
unsigned char *data;
|
||||
size_t size;
|
||||
|
||||
data = read_elf_section_data(fh, shdr, &size);
|
||||
if (!data)
|
||||
return NULL;
|
||||
return g_bytes_new_with_free_func(data, size, free, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* dfi_pv_elf_mem_chunk_read_fn:
|
||||
* @chunk: (not nullable): memory chunk to read from
|
||||
* @chunk_off: offset in the memory chunk to read from
|
||||
* @dst: (not nullable): destination buffer
|
||||
* @size: size in bytes to read
|
||||
*
|
||||
* Memory chunk callback that attempts to read and decrypt up to @size
|
||||
* bytes encrypted memory from @chunk.
|
||||
*/
|
||||
static void dfi_pv_elf_mem_chunk_read_fn(struct dfi_mem_chunk *chunk, u64 chunk_off, void *dst,
|
||||
u64 size)
|
||||
{
|
||||
const pv_elf_ctx_t *ctx = chunk->data;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
g_assert_nonnull(ctx);
|
||||
|
||||
if (pv_elf_read(ctx, chunk_off, dst, size, &error) < 0)
|
||||
ERR_EXIT(_("Reading encrypted memory failed:" ERR_NEWLINE "%s"), error->message);
|
||||
}
|
||||
|
||||
/**
|
||||
* nt_s390_pv_cpu_data_read:
|
||||
* @fh: (not nullable): open input ELF file
|
||||
* @note_hdr: (not nullable): note header of the note to read
|
||||
* @version: expected PV CPU dump version
|
||||
* @dump_key: (not nullable): key to be used to decrypt data stored in the note
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Read and decrypt PV CPU data of the note using @dump_key.
|
||||
*
|
||||
* Returns: #dfi_cpu_t struct on success, %NULL if an error occurred.
|
||||
*/
|
||||
static dfi_cpu_t *nt_s390_pv_cpu_data_read(const struct zg_fh *fh, const Elf64_Nhdr *note_hdr,
|
||||
const unsigned int version, GBytes *dump_key,
|
||||
GError **error)
|
||||
{
|
||||
const size_t note_descsz = note_hdr->n_descsz;
|
||||
g_autofree uint8_t *note_data = NULL;
|
||||
g_autoptr(GBytes) note = NULL;
|
||||
|
||||
g_assert(dump_key);
|
||||
|
||||
note_data = g_malloc(note_descsz);
|
||||
if (nt_read(fh, note_hdr, note_data, note_descsz) < 0) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_NOTE,
|
||||
_("Unable to read confidential CPU data. Dump probably corrupted."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
note = g_bytes_new_take(g_steal_pointer(¬e_data), note_descsz);
|
||||
return pv_decrypt_cpu_note_data(version, note, dump_key, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* pt_notes_add:
|
||||
* @fh: (not nullable): open input file
|
||||
* @phdr: (not nullable): program header of the note segment to add
|
||||
* @version: expected PV CPU dump version
|
||||
* @dump_key: (not nullable): key to be used to decrypt data stored in the notes
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Read all ELF notes for @phdr.
|
||||
*
|
||||
* Returns: %0 on success, -EINVAL if an error occurred.
|
||||
*/
|
||||
static int pt_notes_add(const struct zg_fh *fh, const Elf64_Phdr *phdr, const unsigned int version,
|
||||
GBytes *dump_key, GError **error)
|
||||
{
|
||||
if (phdr->p_offset > OFF_T_MAX) {
|
||||
g_set_error(
|
||||
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_NOTE,
|
||||
_("Confidential CPU data has too large offset. Dump probably corrupted."));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
zg_seek(fh, (off_t)phdr->p_offset, ZG_CHECK);
|
||||
/* Cast to `size_t` is safe because we're using `ZG_CHECK` here. This
|
||||
* means an error in `zg_tell` leads to an exit and therefore `zg_tell`
|
||||
* cannot return an negative value.
|
||||
*/
|
||||
while ((size_t)zg_tell(fh, ZG_CHECK) - phdr->p_offset < phdr->p_filesz) {
|
||||
g_autoptr(dfi_cpu_t) cpu_current = NULL;
|
||||
Elf64_Nhdr note;
|
||||
ssize_t rc;
|
||||
|
||||
rc = zg_read(fh, ¬e, sizeof(note), ZG_CHECK_ERR);
|
||||
if (rc != sizeof(note)) {
|
||||
g_set_error(
|
||||
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_NOTE,
|
||||
_("Confidential CPU data could not be read. Dump probably corrupted."));
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (note.n_type) {
|
||||
case NT_S390_PV_CPU_DATA:
|
||||
cpu_current = nt_s390_pv_cpu_data_read(fh, ¬e, version, dump_key, error);
|
||||
if (!cpu_current)
|
||||
return -EINVAL;
|
||||
dfi_cpu_add(g_steal_pointer(&cpu_current));
|
||||
break;
|
||||
default:
|
||||
util_log_print(UTIL_LOG_WARN, _("Unknown ELF-Note %#x"), note.n_type);
|
||||
__attribute__((fallthrough));
|
||||
case NT_PRSTATUS:
|
||||
case NT_FPREGSET:
|
||||
case NT_S390_TIMER:
|
||||
case NT_S390_TODCMP:
|
||||
case NT_S390_TODPREG:
|
||||
case NT_S390_CTRS:
|
||||
case NT_S390_PREFIX:
|
||||
case NT_S390_VXRS_LOW:
|
||||
case NT_S390_VXRS_HIGH:
|
||||
case NT_S390_GS_CB:
|
||||
/* In case of PV ignore all these note types */
|
||||
nt_skip(fh, ¬e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dfi_pv_elf_init:
|
||||
*
|
||||
* Initialize Protected Virtualization ELF input dump format for @g.fh.
|
||||
*
|
||||
* Returns: %0 on success, -ENODEV in case it's not a PV ELF dump and
|
||||
* -EINVAL in case of an error.
|
||||
*/
|
||||
static int dfi_pv_elf_init(void)
|
||||
{
|
||||
const Elf64_Shdr *completion_shdr = NULL, *storage_state_shdr = NULL;
|
||||
g_autoptr(GBytes) key = NULL, dump_key = NULL, completion_sdata = NULL;
|
||||
g_autoptr(storage_state_mmap_t) storage_state_data = NULL;
|
||||
const pv_dump_completion_v1_t *cpl_conf_v1_decr = NULL;
|
||||
g_autoptr(pv_dump_completion_t) cpl_conf_decr = NULL;
|
||||
const char *key_path = g.opts.key_path;
|
||||
g_autoptr(Elf64_Phdr) phdrs = NULL;
|
||||
g_autoptr(Elf64_Shdr) shdrs = NULL;
|
||||
g_autoptr(Elf64_Ehdr) ehdr = NULL;
|
||||
g_autofree char *shstrtab = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
unsigned int cpu_dump_version;
|
||||
const struct zg_fh *fh = g.fh;
|
||||
unsigned int shnum, phnum;
|
||||
bool load_found = false;
|
||||
size_t shstrtab_size;
|
||||
unsigned int i;
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, _("DFI %s initialization\n"), dfi_pv_elf.name);
|
||||
ehdr = read_elf_hdr(fh);
|
||||
if (!ehdr)
|
||||
return -ENODEV;
|
||||
|
||||
if (ehdr_check_s390x(ehdr) < 0)
|
||||
return -ENODEV;
|
||||
|
||||
shdrs = read_elf_shdrs(fh, ehdr, &shnum);
|
||||
if (!shdrs)
|
||||
return -ENODEV;
|
||||
|
||||
shstrtab = read_elf_shstrtab(fh, ehdr, shdrs, shnum, &shstrtab_size);
|
||||
if (!shstrtab)
|
||||
return -ENODEV;
|
||||
|
||||
if (!pv_is_pv_elf(shdrs, shnum, shstrtab, shstrtab_size))
|
||||
return -ENODEV;
|
||||
|
||||
df_elf_ensure_s390x();
|
||||
dfi_arch_set(DFI_ARCH_64);
|
||||
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
||||
|
||||
/* Try to read the customer communication key */
|
||||
if (!key_path)
|
||||
return -ENOKEY;
|
||||
util_log_print(UTIL_LOG_DEBUG, _("Key path: %s\n"), key_path);
|
||||
key = pv_file_get_content_as_secure_bytes(key_path);
|
||||
if (!key)
|
||||
return -ENOKEY;
|
||||
|
||||
/* Find PV completion configuration data and storage state data sections */
|
||||
completion_shdr = find_elf_shdr_by_name(shdrs, shnum, shstrtab, shstrtab_size,
|
||||
PV_ELF_SECTION_NAME_COMPL);
|
||||
if (!completion_shdr)
|
||||
return -EINVAL;
|
||||
|
||||
storage_state_shdr = find_elf_shdr_by_name(shdrs, shnum, shstrtab, shstrtab_size,
|
||||
PV_ELF_SECTION_NAME_TWEAKS);
|
||||
if (!storage_state_shdr)
|
||||
return -EINVAL;
|
||||
|
||||
/* Read the PV completion configuration section data */
|
||||
completion_sdata = read_elf_section_data_as_gbytes(fh, completion_shdr);
|
||||
if (!completion_sdata)
|
||||
return -EINVAL;
|
||||
|
||||
/* Derive and store the dump key in `@dump_key`. This key is used to AES-GCM decrypt the
|
||||
* completion configuration data and store it in `@cpl_conf_decr`. In addition, mmap the
|
||||
* configuration storage state area and use the `tweak_nonce`.
|
||||
*/
|
||||
if (pv_process_section_data(fh->fh, completion_sdata, storage_state_shdr->sh_offset,
|
||||
storage_state_shdr->sh_size, key, &cpl_conf_decr, &dump_key,
|
||||
&storage_state_data, &error) < 0) {
|
||||
ERR(_("Unable to read decryption information:" ERR_NEWLINE "%s."), error->message);
|
||||
return -EINVAL;
|
||||
}
|
||||
g_assert_nonnull(cpl_conf_decr);
|
||||
g_assert_cmpuint(cpl_conf_decr->version, ==, PV_COMPL_DATA_VERSION_1);
|
||||
|
||||
cpl_conf_v1_decr = (pv_dump_completion_v1_t *)cpl_conf_decr;
|
||||
cpu_dump_version = PV_SEC_CPU_DATA_VERSION_1;
|
||||
phdrs = read_elf_phdrs(fh, ehdr, &phnum);
|
||||
util_log_print(UTIL_LOG_DEBUG, _("DFI %s e_phnum %u\n"), dfi_pv_elf.name, phnum);
|
||||
for (i = 0; i < phnum; i++) {
|
||||
const Elf64_Phdr *phdr = &phdrs[i];
|
||||
|
||||
util_log_print(UTIL_LOG_DEBUG, _("DFI %s p_type[%d] 0x%lx\n"), dfi_pv_elf.name, i,
|
||||
phdr->p_type);
|
||||
switch (phdr->p_type) {
|
||||
case PT_LOAD: {
|
||||
/* Initialize callback data for the `dfi_pv_elf_mem_chunk_read_fn` function
|
||||
*/
|
||||
g_autoptr(pv_elf_ctx_t) elf_ctx =
|
||||
pv_elf_ctx_new(fh->fh, &cpl_conf_v1_decr->data.confidential_area,
|
||||
storage_state_data, phdr->p_offset, &error);
|
||||
if (!elf_ctx) {
|
||||
ERR(_("Reading dump failed:" ERR_NEWLINE "%s"), error->message);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (load_found) {
|
||||
ERR(_("Reading dump failed:" ERR_NEWLINE
|
||||
"Multiple PT_LOAD segments are not supported."));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pt_load_add(fh, phdr, (void **)&elf_ctx, dfi_pv_elf_mem_chunk_read_fn,
|
||||
(dfi_mem_chunk_free_fn)pv_elf_ctx_free) < 0)
|
||||
return -EINVAL;
|
||||
load_found = true;
|
||||
break;
|
||||
}
|
||||
case PT_NOTE:
|
||||
/* Add CPU information (decrypts the CPU data) */
|
||||
if (pt_notes_add(fh, phdr, cpu_dump_version, dump_key, &error)) {
|
||||
ERR(_("Reading dump failed:" ERR_NEWLINE "%s"), error->message);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
util_log_print(UTIL_LOG_WARN, _("Unknown ELF-PHDR type %#x"), phdr->p_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dfi_attr_version_set(cpl_conf_decr->version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dfi_pv_elf_cleanup(void)
|
||||
{
|
||||
/* nothing to do here */
|
||||
}
|
||||
|
||||
/*
|
||||
* PV ELF DFI operations
|
||||
*/
|
||||
struct dfi dfi_pv_elf = {
|
||||
.exit = dfi_pv_elf_cleanup,
|
||||
.feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
|
||||
.init = dfi_pv_elf_init,
|
||||
.name = "ELF (protected virtualization dump)",
|
||||
};
|
||||
@@ -132,7 +132,7 @@ void dfi_vmcoreinfo_init(void)
|
||||
/*
|
||||
* Return vmcoreinfo data
|
||||
*/
|
||||
char *dfi_vmcoreinfo_get(void)
|
||||
const char *dfi_vmcoreinfo_get(void)
|
||||
{
|
||||
return l.vmcoreinfo;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define DFI_VMCOREINFO_H
|
||||
|
||||
void dfi_vmcoreinfo_init(void);
|
||||
char *dfi_vmcoreinfo_get(void);
|
||||
const char *dfi_vmcoreinfo_get(void);
|
||||
int dfi_vmcoreinfo_tag(char *str, int len, const char *sym);
|
||||
int dfi_vmcoreinfo_symbol(unsigned long *val, const char *sym);
|
||||
int dfi_vmcoreinfo_offset(unsigned long *offs, const char *sym);
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "dfi_vmcoreinfo.h"
|
||||
#include "dfo.h"
|
||||
|
||||
#define HDR_PER_CPU_SIZE 0x4a0
|
||||
#define HDR_PER_MEMC_SIZE 0x100
|
||||
#define HDR_BASE_SIZE 0x2000
|
||||
|
||||
@@ -81,6 +80,8 @@ static void *notes_init(Elf64_Phdr *phdr, void *segment_start, u64 elf_offset)
|
||||
ptr = nt_s390_vxrs_low(ptr, cpu);
|
||||
ptr = nt_s390_vxrs_high(ptr, cpu);
|
||||
}
|
||||
if (dfi_cpu_content_fac_check(DFI_CPU_CONTENT_FAC_GS))
|
||||
ptr = nt_s390_gs_cb(ptr, cpu);
|
||||
}
|
||||
out:
|
||||
ptr = nt_vmcoreinfo(ptr, dfi_vmcoreinfo_get());
|
||||
@@ -133,9 +134,8 @@ static void dfo_elf_init(void)
|
||||
u64 hdr_off;
|
||||
|
||||
ensure_s390x();
|
||||
alloc_size = HDR_BASE_SIZE +
|
||||
dfi_cpu_cnt() * HDR_PER_CPU_SIZE +
|
||||
dfi_mem_chunk_cnt() * HDR_PER_MEMC_SIZE;
|
||||
alloc_size = HDR_BASE_SIZE + dfi_cpu_cnt() * get_max_note_size_per_cpu() +
|
||||
dfi_mem_chunk_cnt() * HDR_PER_MEMC_SIZE;
|
||||
buf = zg_alloc(alloc_size);
|
||||
/* Init elf header */
|
||||
ptr = ehdr_init(buf, dfi_mem_chunk_cnt() + 1);
|
||||
|
||||
41
zdump/opts.c
41
zdump/opts.c
@@ -20,9 +20,11 @@
|
||||
#include "opts.h"
|
||||
|
||||
static struct option long_opts[] = {
|
||||
/* clang-format off */
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
{"info", no_argument, NULL, 'i'},
|
||||
{"key", required_argument, NULL, 'k'},
|
||||
{"device", no_argument, NULL, 'd'},
|
||||
{"mount", no_argument, NULL, 'm'},
|
||||
{"umount", no_argument, NULL, 'u'},
|
||||
@@ -30,19 +32,20 @@ static struct option long_opts[] = {
|
||||
{"select", required_argument, NULL, 's'},
|
||||
{"debug", no_argument, NULL, 'X'},
|
||||
{"verbose", no_argument, NULL, 'V'},
|
||||
{NULL, 0, NULL, 0 }
|
||||
{NULL, 0, NULL, 0 },
|
||||
/* clang-format on */
|
||||
};
|
||||
|
||||
static const char optstr[] = "hvVidmus:f:X";
|
||||
static const char optstr[] = "hvVidmuk:s:f:X";
|
||||
|
||||
/*
|
||||
* Text for --help option
|
||||
*/
|
||||
static const char help_text[] =
|
||||
"Usage: zgetdump DUMP [-s SYS] [-f FMT] > DUMP_FILE\n"
|
||||
" DUMP [-s SYS] [-f FMT] DUMP_FILE\n"
|
||||
" -m DUMP [-s SYS] [-f FMT] DIR\n"
|
||||
" -i DUMP [-s SYS]\n"
|
||||
"Usage: zgetdump DUMP [-s SYS] [-f FMT] [-k KEY] > DUMP_FILE\n"
|
||||
" DUMP [-s SYS] [-f FMT] [-k KEY] DUMP_FILE\n"
|
||||
" -m DUMP [-s SYS] [-f FMT] [-k KEY] DIR\n"
|
||||
" -i DUMP [-s SYS] [-k KEY]\n"
|
||||
" -d DUMPDEV\n"
|
||||
" -u DIR\n"
|
||||
"\n"
|
||||
@@ -58,6 +61,7 @@ static const char help_text[] =
|
||||
"In the syntax description, DUMP specifies a dump device or dump file to be\n"
|
||||
"read. The following options are available:\n"
|
||||
"\n"
|
||||
"-k, --key Specify the key KEY to decrypt the protected virtualization dump\n"
|
||||
"-m, --mount Mount DUMP to mount point DIR\n"
|
||||
"-u, --umount Unmount dump from mount point DIR\n"
|
||||
"-i, --info Print DUMP information\n"
|
||||
@@ -71,7 +75,7 @@ static const char help_text[] =
|
||||
" messages. This option is intended for debugging\n"
|
||||
"-h, --help Print this help, then exit\n";
|
||||
|
||||
static const char copyright_str[] = "Copyright IBM Corp. 2001, 2018";
|
||||
static const char copyright_str[] = "Copyright IBM Corp. 2001, 2022";
|
||||
|
||||
/*
|
||||
* Select option strings
|
||||
@@ -88,6 +92,7 @@ static void init_defaults(struct options *opts)
|
||||
opts->prog_name = "zgetdump";
|
||||
opts->action = ZG_ACTION_COPY;
|
||||
opts->output_path = NULL;
|
||||
opts->key_path = NULL;
|
||||
#ifdef __s390x__
|
||||
opts->fmt = "elf";
|
||||
#else
|
||||
@@ -101,10 +106,15 @@ static void init_defaults(struct options *opts)
|
||||
/*
|
||||
* Print "help" hint
|
||||
*/
|
||||
static void __noreturn print_usage_exit(const char *prog_name)
|
||||
void __noreturn print_usage_exit(const char *prog_name)
|
||||
{
|
||||
opts_print_usage(prog_name);
|
||||
zg_exit(1);
|
||||
}
|
||||
|
||||
void opts_print_usage(const char *prog_name)
|
||||
{
|
||||
STDERR("Try '%s --help' for more information.\n", prog_name);
|
||||
zg_exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -176,6 +186,16 @@ static void output_set(struct options *opts, const char *path)
|
||||
opts->output_path = zg_strdup(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set customer communication key (CCK)
|
||||
*/
|
||||
static void key_set(struct options *opts, const char *key)
|
||||
{
|
||||
assert(!opts->key_path);
|
||||
|
||||
opts->key_path = zg_strdup(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set FUSE debug options
|
||||
*/
|
||||
@@ -313,6 +333,9 @@ void opts_parse(int argc, char *argv[], struct options *opts)
|
||||
case 's':
|
||||
select_set(opts, optarg);
|
||||
break;
|
||||
case 'k':
|
||||
key_set(opts, optarg);
|
||||
break;
|
||||
case 'X':
|
||||
opts->debug_specified = 1;
|
||||
break;
|
||||
|
||||
@@ -20,6 +20,7 @@ struct options {
|
||||
char *device;
|
||||
/* If `output_path == NULL` the output is written to `stdout` */
|
||||
const char *output_path;
|
||||
const char *key_path;
|
||||
char *mount_point;
|
||||
int fmt_specified;
|
||||
const char *fmt;
|
||||
@@ -36,5 +37,7 @@ extern const char *OPTS_SELECT_PROD;
|
||||
extern const char *OPTS_SELECT_ALL;
|
||||
|
||||
void opts_parse(int argc, char *argv[], struct options *opts);
|
||||
void opts_print_usage(const char *prog_name);
|
||||
void __noreturn print_usage_exit(const char *prog_name);
|
||||
|
||||
#endif /* OPTS_H */
|
||||
|
||||
218
zdump/pv_defs.h
Normal file
218
zdump/pv_defs.h
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Secure execution/PV guest dump definitions.
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef PV_DEFS_H
|
||||
#define PV_DEFS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
/* Special ELF section names used for Protected Virtualization Dumps */
|
||||
#define PV_ELF_SECTION_NAME_COMPL "pv_compl"
|
||||
#define PV_ELF_SECTION_NAME_TWEAKS "pv_mem_meta"
|
||||
|
||||
#define PV_COMPL_DATA_VERSION_1 ((uint32_t)1)
|
||||
#define PV_SEC_CPU_DATA_VERSION_1 ((uint32_t)1)
|
||||
|
||||
/**
|
||||
* pv_tweak_nonce_t:
|
||||
*
|
||||
* Struct representing a tweak nonce.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t value[16];
|
||||
} pv_tweak_nonce_t __big_endian;
|
||||
|
||||
/**
|
||||
* pv_dump_completion_aad_v1_t:
|
||||
*
|
||||
* Struct representing the authenticated part of a PV dump completion v1
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t version; /* 0000 */
|
||||
uint32_t len; /* 0004 */
|
||||
uint64_t res_0x8; /* 0008 */
|
||||
uint8_t seed[64]; /* 0016 */
|
||||
uint8_t iv[12]; /* 0080 */
|
||||
uint32_t res_0x92; /* 0092 */
|
||||
} pv_dump_completion_aad_v1_t __big_endian;
|
||||
STATIC_ASSERT(sizeof(pv_dump_completion_aad_v1_t) == 96)
|
||||
|
||||
/**
|
||||
* pv_dump_completion_confidential_area_v1_t:
|
||||
*
|
||||
* Struct representing the encrypted part of a PV dump completion v1
|
||||
*/
|
||||
typedef struct {
|
||||
pv_tweak_nonce_t nonce; /* 0096 */
|
||||
union {
|
||||
uint8_t key[64]; /* 0112 */
|
||||
struct {
|
||||
uint8_t key1[32]; /* 0112 */
|
||||
uint8_t key2[32]; /* 0144 */
|
||||
};
|
||||
};
|
||||
uint8_t res_0x176[96]; /* 0176 */
|
||||
} pv_dump_completion_confidential_area_v1_t __big_endian;
|
||||
STATIC_ASSERT(sizeof(pv_dump_completion_confidential_area_v1_t) == 176)
|
||||
|
||||
/**
|
||||
* pv_dump_completion_data_v1_t
|
||||
*
|
||||
* Struct to interpret the data returned by the `Complete Configuration Dump` UVC.
|
||||
*/
|
||||
typedef struct {
|
||||
pv_dump_completion_aad_v1_t aad; /* 0000 */
|
||||
pv_dump_completion_confidential_area_v1_t confidential_area; /* 0096 */
|
||||
uint8_t tag[16]; /* 0272 */
|
||||
/* 0288 */
|
||||
} pv_dump_completion_data_v1_t __big_endian;
|
||||
STATIC_ASSERT(sizeof(pv_dump_completion_data_v1_t) == 288)
|
||||
|
||||
/* Container data structures used to implement polymorphic behavior */
|
||||
#define PV_STRUCT_NAME(_struct, _version) _struct##_v##_version##_t
|
||||
#define PV_GET_STRUCT_DATA(_struct, _version, _container) \
|
||||
_container->version != _version ? \
|
||||
NULL : \
|
||||
&(((PV_STRUCT_NAME(_struct, _version) *)_container)->data)
|
||||
|
||||
#define PV_GET_DUMP_DATA_V1(_container) PV_GET_STRUCT_DATA(pv_dump_completion, 1, _container)
|
||||
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
} pv_dump_completion_t;
|
||||
|
||||
typedef struct {
|
||||
pv_dump_completion_t super;
|
||||
pv_dump_completion_data_v1_t data;
|
||||
} PV_STRUCT_NAME(pv_dump_completion, 1);
|
||||
|
||||
/**
|
||||
* pv_cpu_vector_register_t:
|
||||
*
|
||||
* Struct representing the PV CPU vector register
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t low;
|
||||
uint64_t high;
|
||||
} pv_cpu_vector_register_t __big_endian;
|
||||
STATIC_ASSERT(sizeof(pv_cpu_vector_register_t) == 16)
|
||||
|
||||
/**
|
||||
* pv_cpu_dump_aad_v1_t:
|
||||
*
|
||||
* Struct representing the authenticated part of a PV cpu dump v1
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t version; /* 0000 */
|
||||
uint32_t len; /* 0004 */
|
||||
uint8_t iv[12]; /* 0008 */
|
||||
uint8_t res_0x20[12]; /* 0020 */
|
||||
} pv_cpu_dump_aad_v1_t __big_endian;
|
||||
STATIC_ASSERT(sizeof(pv_cpu_dump_aad_v1_t) == 32)
|
||||
|
||||
/**
|
||||
* pv_cpu_dump_confidential_area_v1_t:
|
||||
*
|
||||
* Struct to interpret the Secure CPU Dump Area UVC.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t gprs[128]; /* 0032 */
|
||||
uint8_t psw[16]; /* 0160 */
|
||||
uint8_t res_0x176[8]; /* 0176 */
|
||||
uint32_t prefix; /* 0184 */
|
||||
uint32_t fpc; /* 0188 */
|
||||
uint8_t res_0x192[4]; /* 0192 */
|
||||
uint32_t todpreg; /* 0196 */
|
||||
uint64_t timer; /* 0200 */
|
||||
uint64_t todcmp; /* 0208 */
|
||||
uint8_t res_0x216[8]; /* 0216 */
|
||||
uint8_t acrs[64]; /* 0224 */
|
||||
uint8_t ctrs[128]; /* 0288 */
|
||||
struct {
|
||||
pv_cpu_vector_register_t vector_register_low[16];
|
||||
uint8_t vector_register_high[256];
|
||||
}; /* 0416 */
|
||||
uint8_t res_0x928[512]; /* 0928 */
|
||||
uint8_t zeros_2[8]; /* 1440 */
|
||||
uint64_t gsd; /* 1448 */
|
||||
uint64_t gssm; /* 1456 */
|
||||
uint64_t gs_epl_a; /* 1464 */
|
||||
uint8_t res_0x1472[64]; /* 1472 */
|
||||
union {
|
||||
struct {
|
||||
uint16_t has_osii : 1;
|
||||
uint16_t reserved : 15;
|
||||
};
|
||||
uint16_t dump_flags; /* 1536 */
|
||||
};
|
||||
uint8_t res_0x1538[6]; /* 1538 */
|
||||
uint8_t res_0x1544[8]; /* 1544 */
|
||||
} pv_cpu_dump_confidential_area_v1_t __big_endian;
|
||||
STATIC_ASSERT(sizeof(pv_cpu_dump_confidential_area_v1_t) == 1520)
|
||||
|
||||
/**
|
||||
* pv_cpu_dump_v1_t:
|
||||
*
|
||||
* Struct representing a PV CPU dump v1 returned by the `Dump CPU state` UVC.
|
||||
*/
|
||||
typedef struct {
|
||||
pv_cpu_dump_aad_v1_t aad; /* 0000 */
|
||||
pv_cpu_dump_confidential_area_v1_t confidential_area; /* 0032 */
|
||||
uint8_t tag[16]; /* 1552 */
|
||||
} pv_cpu_dump_v1_t __big_endian;
|
||||
STATIC_ASSERT(sizeof(pv_cpu_dump_v1_t) == 1568)
|
||||
|
||||
/* PV special tweak component indicator */
|
||||
#define PV_SPECIAL_INDICATOR 0xFFFFFFFFUL
|
||||
|
||||
/**
|
||||
* pv_special_tweak_component_layout_t:
|
||||
*
|
||||
* Struct representing the layout of a special tweak component.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t indicator;
|
||||
uint8_t reserved[10];
|
||||
uint8_t flag_reserved1;
|
||||
uint8_t flag_reserved2 : 5;
|
||||
uint8_t is_mapped_page : 1;
|
||||
uint8_t is_shared_page : 1;
|
||||
uint8_t is_zero_page : 1;
|
||||
} pv_special_tweak_component_layout_t __big_endian;
|
||||
STATIC_ASSERT(sizeof(pv_special_tweak_component_layout_t) == 16)
|
||||
|
||||
/**
|
||||
* pv_tweak_component_t:
|
||||
*
|
||||
* Struct representing a tweak component returned by `Dump Configuration Storage State` UVC.
|
||||
*/
|
||||
typedef union {
|
||||
uint8_t value[16];
|
||||
pv_special_tweak_component_layout_t special;
|
||||
} pv_tweak_component_t __big_endian;
|
||||
STATIC_ASSERT(sizeof(pv_tweak_component_t) == 16)
|
||||
|
||||
/**
|
||||
* pv_tweak_t:
|
||||
*
|
||||
* Struct representing one tweak value.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t value[16];
|
||||
} pv_tweak_t;
|
||||
STATIC_ASSERT(sizeof(pv_tweak_t) == 16)
|
||||
|
||||
STATIC_ASSERT(sizeof_field(pv_tweak_t, value) == sizeof_field(pv_tweak_component_t, value))
|
||||
STATIC_ASSERT(sizeof_field(pv_tweak_t, value) == sizeof_field(pv_tweak_nonce_t, value))
|
||||
|
||||
#endif /* PV_DEFS_H */
|
||||
980
zdump/pv_utils.c
Normal file
980
zdump/pv_utils.c
Normal file
@@ -0,0 +1,980 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Utilities to decrypt secure execution guest dumps.
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2021
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
#include "pv_utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/kdf.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "lib/util_log.h"
|
||||
#include "libpv/crypto.h"
|
||||
#include "libpv/se-hdr.h"
|
||||
#include "pv_defs.h"
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf64_Phdr, free)
|
||||
|
||||
/* Definitions for decrypting memory and deriving dump key */
|
||||
#define PV_CSS_PAGESIZE 0x1000U /* Configuration storage state page size */
|
||||
#define PV_DUMP_V1_HKDF_INFO "IBM Z Ultravisor Dump"
|
||||
#define PV_DUMP_V1_HKDF_LEN 32
|
||||
#define PV_DUMP_V1_HKDF_FUN EVP_sha512()
|
||||
#define PV_DUMP_V1_CIPHER EVP_aes_256_gcm()
|
||||
|
||||
static gboolean u64_checked_add(u64 *res, u64 lhs, u64 rhs)
|
||||
{
|
||||
guint64 _res = 0;
|
||||
gboolean success = g_uint64_checked_add(&_res, (guint64)lhs, (guint64)rhs);
|
||||
|
||||
*res = (u64)_res;
|
||||
return success;
|
||||
}
|
||||
|
||||
static u64 page_offset(u64 addr)
|
||||
{
|
||||
return addr % PV_CSS_PAGESIZE;
|
||||
}
|
||||
|
||||
static u64 page_index(u64 addr)
|
||||
{
|
||||
return addr / PV_CSS_PAGESIZE;
|
||||
}
|
||||
|
||||
static u64 page_start_addr(u64 page_idx, GError **error)
|
||||
{
|
||||
gboolean success;
|
||||
uint64_t ret;
|
||||
|
||||
success = g_uint64_checked_mul(&ret, page_idx, PV_CSS_PAGESIZE);
|
||||
if (G_UNLIKELY(!success)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PAGE_START_ADDR_OVERFLOW,
|
||||
_("UInt overflow detected: %s: pageidx: %#llx"), __func__, page_idx);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u64 page_end_addr(u64 page_idx, GError **error)
|
||||
{
|
||||
gboolean success;
|
||||
uint64_t ret;
|
||||
|
||||
/* (page_idx + 1) * PV_CSS_PAGESIZE - 1; */
|
||||
success = g_uint64_checked_add(&ret, page_idx, 1);
|
||||
success &= g_uint64_checked_mul(&ret, ret, PV_CSS_PAGESIZE);
|
||||
ret -= 1;
|
||||
if (G_UNLIKELY(!success)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PAGE_END_ADDR_OVERFLOW,
|
||||
_("UInt overflow detected: %s: pageidx: %#llx"), __func__, page_idx);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pv_dump_completion_v1_free(pv_dump_completion_v1_t *cpl)
|
||||
{
|
||||
pv_dump_completion_free((pv_dump_completion_t *)cpl);
|
||||
}
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(pv_dump_completion_v1_t, pv_dump_completion_v1_free)
|
||||
|
||||
void pv_dump_completion_free(pv_dump_completion_t *cpl)
|
||||
{
|
||||
if (!cpl)
|
||||
return;
|
||||
|
||||
if (cpl->version == PV_COMPL_DATA_VERSION_1) {
|
||||
pv_dump_completion_v1_t *cpl_v1 = (pv_dump_completion_v1_t *)cpl;
|
||||
OPENSSL_cleanse(&cpl_v1->data, sizeof(cpl_v1->data));
|
||||
}
|
||||
g_free(cpl);
|
||||
}
|
||||
|
||||
GBytes *pv_derive_dump_key_v1(const pv_dump_completion_data_v1_t *cpl_data, GBytes *cck,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GBytes) salt = NULL, info = NULL;
|
||||
size_t cck_size;
|
||||
size_t exp_cck_size = sizeof_field(struct pv_hdr_encrypted, cust_comm_key);
|
||||
|
||||
assert(cpl_data->aad.version == PV_COMPL_DATA_VERSION_1);
|
||||
|
||||
cck_size = g_bytes_get_size(cck);
|
||||
if (cck_size != exp_cck_size) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_CCK_SIZE,
|
||||
_("Wrong key size: expected %lu != actual %lu"), exp_cck_size,
|
||||
cck_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
salt = g_bytes_new(&cpl_data->aad.seed, sizeof(cpl_data->aad.seed));
|
||||
info = g_bytes_new(PV_DUMP_V1_HKDF_INFO, strlen(PV_DUMP_V1_HKDF_INFO));
|
||||
return pv_hkdf_extract_and_expand(PV_DUMP_V1_HKDF_LEN, cck, salt, info, PV_DUMP_V1_HKDF_FUN,
|
||||
error);
|
||||
}
|
||||
|
||||
pv_dump_completion_v1_t *pv_decrypt_dump_completion_v1(const pv_dump_completion_data_v1_t *cpl_data,
|
||||
GBytes *dump_key, GError **error)
|
||||
{
|
||||
g_autoptr(GBytes) encr = NULL, aad = NULL, tag = NULL, decr = NULL, iv = NULL;
|
||||
g_autoptr(pv_dump_completion_v1_t) cpl = NULL;
|
||||
PvCipherParms params;
|
||||
size_t copied;
|
||||
void *tmp;
|
||||
|
||||
assert(cpl_data->aad.version == PV_COMPL_DATA_VERSION_1);
|
||||
|
||||
encr = g_bytes_new(&cpl_data->confidential_area, sizeof(cpl_data->confidential_area));
|
||||
aad = g_bytes_new(&cpl_data->aad, sizeof(cpl_data->aad));
|
||||
tag = g_bytes_new(&cpl_data->tag, sizeof(cpl_data->tag));
|
||||
iv = g_bytes_new(&cpl_data->aad.iv, sizeof(cpl_data->aad.iv));
|
||||
params.cipher = PV_DUMP_V1_CIPHER;
|
||||
params.key = dump_key;
|
||||
params.iv = iv;
|
||||
params.tag_size = g_bytes_get_size(tag);
|
||||
if (pv_gcm_decrypt(encr, aad, tag, ¶ms, &decr, error) < 0)
|
||||
return NULL;
|
||||
|
||||
cpl = g_malloc(sizeof(*cpl));
|
||||
cpl->super.version = PV_COMPL_DATA_VERSION_1;
|
||||
tmp = pv_gbytes_memcpy(&cpl->data.aad, sizeof(cpl->data.aad), aad, &copied);
|
||||
if (!tmp || copied != sizeof(cpl->data.aad)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_AAD_SIZE,
|
||||
_("Wrong AAD size"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tmp = pv_gbytes_memcpy(&cpl->data.confidential_area, sizeof(cpl->data.confidential_area),
|
||||
decr, &copied);
|
||||
if (!tmp || copied != sizeof(cpl->data.confidential_area)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_CONFIDENTIAL_SIZE,
|
||||
_("Wrong confidential data size"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tmp = pv_gbytes_memcpy(&cpl->data.tag, sizeof(cpl->data.tag), tag, &copied);
|
||||
if (!tmp || copied != sizeof(cpl->data.tag)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_TAG_SIZE,
|
||||
_("Wrong tag size"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&cpl);
|
||||
}
|
||||
|
||||
static long pv_cpu_note_data_get_version(GBytes *note, GError **error)
|
||||
{
|
||||
size_t size;
|
||||
const uint32_t *version = g_bytes_get_data(note, &size);
|
||||
STATIC_ASSERT(offsetof(pv_cpu_dump_aad_v1_t, version) == 0);
|
||||
|
||||
/* check whether we can dereference @version */
|
||||
if (sizeof(*version) > size) {
|
||||
g_set_error(
|
||||
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_NOTE_SIZE, "%s",
|
||||
_("Confidential CPU data has incorrect size. Dump probably corrupted."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return *version;
|
||||
}
|
||||
|
||||
dfi_cpu_t *pv_decrypt_cpu_note_data(const unsigned int expected_version, GBytes *cpu_note,
|
||||
GBytes *dump_key, GError **error)
|
||||
{
|
||||
long version = pv_cpu_note_data_get_version(cpu_note, error);
|
||||
if (version < 0)
|
||||
return NULL;
|
||||
|
||||
if (version != expected_version) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_UNSUPP_SEC_CPU_VER,
|
||||
_("Wrong NT_S390_PV_CPU_DATA version (%ld)"), version);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (version) {
|
||||
case PV_SEC_CPU_DATA_VERSION_1: {
|
||||
const pv_cpu_dump_confidential_area_v1_t *pv_cpu;
|
||||
const pv_cpu_dump_v1_t *cpu_encrypted;
|
||||
size_t cpu_note_size, cpu_decrypted_size;
|
||||
g_autoptr(GBytes) cpu_decrypted = NULL;
|
||||
|
||||
cpu_encrypted = g_bytes_get_data(cpu_note, &cpu_note_size);
|
||||
if (sizeof(*cpu_encrypted) > cpu_note_size) {
|
||||
g_set_error(
|
||||
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_NOTE_SIZE,
|
||||
_("Confidential CPU data has incorrect size. Dump probably corrupted."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cpu_decrypted = pv_decrypt_cpu_dump_area_v1(cpu_encrypted, dump_key, error);
|
||||
if (!cpu_decrypted) {
|
||||
g_prefix_error(
|
||||
error,
|
||||
_("Unable to authenticate confidential CPU data. Dump probably corrupted:" ERR_NEWLINE));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pv_cpu = g_bytes_get_data(cpu_decrypted, &cpu_decrypted_size);
|
||||
if (cpu_decrypted_size != sizeof(*pv_cpu)) {
|
||||
g_set_error(
|
||||
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_WRONG_NOTE_SIZE,
|
||||
_("Confidential CPU data has incorrect size. Dump probably corrupted."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check dump flags */
|
||||
if (pv_cpu->has_osii)
|
||||
util_log_print(UTIL_LOG_WARN,
|
||||
_("CPU state may contain partial instruction results"));
|
||||
|
||||
return pv_dfi_cpu_from_pv_cpu(pv_cpu);
|
||||
}
|
||||
default:
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_UNSUPP_SEC_CPU_VER,
|
||||
_("Unsupported NT_S390_PV_CPU_DATA version (%ld)"), version);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
GBytes *pv_decrypt_cpu_dump_area_v1(const pv_cpu_dump_v1_t *cpu_dump_area, GBytes *dump_key,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GBytes) encr = NULL, aad = NULL, tag = NULL;
|
||||
g_autoptr(GBytes) iv = NULL, out = NULL;
|
||||
PvCipherParms params;
|
||||
|
||||
encr = g_bytes_new(&cpu_dump_area->confidential_area,
|
||||
sizeof(cpu_dump_area->confidential_area));
|
||||
aad = g_bytes_new(&cpu_dump_area->aad, sizeof(cpu_dump_area->aad));
|
||||
tag = g_bytes_new(&cpu_dump_area->tag, sizeof(cpu_dump_area->tag));
|
||||
iv = g_bytes_new(&cpu_dump_area->aad.iv, sizeof(cpu_dump_area->aad.iv));
|
||||
params.cipher = PV_DUMP_V1_CIPHER;
|
||||
params.key = dump_key;
|
||||
params.iv = iv;
|
||||
params.tag_size = g_bytes_get_size(tag);
|
||||
if (pv_gcm_decrypt(encr, aad, tag, ¶ms, &out, error) < 0)
|
||||
return NULL;
|
||||
return g_steal_pointer(&out);
|
||||
}
|
||||
|
||||
dfi_cpu_t *pv_dfi_cpu_from_pv_cpu(const pv_cpu_dump_confidential_area_v1_t *pv_cpu)
|
||||
{
|
||||
g_autoptr(dfi_cpu_t) ret = dfi_cpu_alloc();
|
||||
|
||||
STATIC_ASSERT(sizeof(ret->gprs) == sizeof(pv_cpu->gprs));
|
||||
(void)memcpy(ret->gprs, pv_cpu->gprs, sizeof(ret->gprs));
|
||||
STATIC_ASSERT(sizeof(ret->psw) == sizeof(pv_cpu->psw));
|
||||
(void)memcpy(ret->psw, pv_cpu->psw, sizeof(ret->psw));
|
||||
|
||||
ret->prefix = pv_cpu->prefix;
|
||||
ret->fpc = pv_cpu->fpc;
|
||||
ret->todpreg = pv_cpu->todpreg;
|
||||
ret->timer = pv_cpu->timer;
|
||||
ret->todcmp = pv_cpu->todcmp;
|
||||
|
||||
STATIC_ASSERT(sizeof(ret->acrs) == sizeof(pv_cpu->acrs));
|
||||
(void)memcpy(ret->acrs, pv_cpu->acrs, sizeof(ret->acrs));
|
||||
|
||||
STATIC_ASSERT(sizeof(ret->ctrs) == sizeof(pv_cpu->ctrs));
|
||||
(void)memcpy(ret->ctrs, pv_cpu->ctrs, sizeof(ret->ctrs));
|
||||
|
||||
/* Copy floating point register and the high part of the first 16 vector
|
||||
* register
|
||||
*/
|
||||
STATIC_ASSERT(ARRAY_SIZE(ret->fprs) == ARRAY_SIZE(pv_cpu->vector_register_low));
|
||||
STATIC_ASSERT(ARRAY_SIZE(pv_cpu->vector_register_low) == ARRAY_SIZE(ret->vxrs_low));
|
||||
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(ret->fprs); i++) {
|
||||
ret->fprs[i] = pv_cpu->vector_register_low[i].low;
|
||||
ret->vxrs_low[i] = pv_cpu->vector_register_low[i].high;
|
||||
}
|
||||
|
||||
STATIC_ASSERT(sizeof(ret->vxrs_high) == sizeof(pv_cpu->vector_register_high));
|
||||
(void)memcpy(ret->vxrs_high, pv_cpu->vector_register_high, sizeof(ret->vxrs_high));
|
||||
|
||||
/* Set guarded storage registers */
|
||||
ret->reserved = 0;
|
||||
ret->gsd = pv_cpu->gsd;
|
||||
ret->gssm = pv_cpu->gssm;
|
||||
ret->gs_epl_a = pv_cpu->gs_epl_a;
|
||||
/* Add GS facility */
|
||||
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_GS);
|
||||
|
||||
/* NOTE: In the future it might be useful to store `@pv_cpu->dump_flags`
|
||||
* in the `struct dfi_cpu`. Currently, we don't have any use case for
|
||||
* it.
|
||||
*/
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
/* Utilities for decrypting the memory */
|
||||
|
||||
struct _pv_crypto_ctx {
|
||||
EVP_CIPHER_CTX *cipher_ctx;
|
||||
BIO *input;
|
||||
BIO *filter;
|
||||
/* To be allocated/deallocated using OpenSSL malloc and clear+free */
|
||||
pv_tweak_nonce_t *nonce;
|
||||
|
||||
/* scratch area */
|
||||
pv_tweak_t tweak_scratch;
|
||||
};
|
||||
|
||||
static BIO *pv_BIO_cipher_new(const EVP_CIPHER *cipher, const unsigned char *key, size_t key_len,
|
||||
enum PvCryptoMode mode, GError **error)
|
||||
{
|
||||
bool encrypt = mode == PV_ENCRYPT;
|
||||
EVP_CIPHER_CTX *ctx = NULL;
|
||||
g_autoptr(BIO) ret = NULL;
|
||||
ENGINE *engine = NULL;
|
||||
|
||||
ret = BIO_new(BIO_f_cipher());
|
||||
if (!ret) {
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (BIO_get_cipher_ctx(ret, &ctx) != 1) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO_FAIL,
|
||||
_("BIO_get_cipher_ctx failed"));
|
||||
return NULL;
|
||||
}
|
||||
g_assert(ctx);
|
||||
|
||||
if (EVP_CipherInit_ex(ctx, cipher, engine, NULL, NULL, encrypt) != 1) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO_FAIL,
|
||||
_("EVP_Cipher_init failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check key length */
|
||||
if (EVP_CIPHER_CTX_key_length(ctx) != (int)key_len) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO_KEY,
|
||||
_("Passed key has incorrect size: %ld != %d"), key_len,
|
||||
EVP_CIPHER_key_length(cipher));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set key */
|
||||
if (EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, -1) != 1) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO_FAIL,
|
||||
_("EVP_Cipher_init set_key failed"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
pv_crypto_ctx_t *pv_crypto_ctx_new(BIO *input, const unsigned char *key, size_t key_size,
|
||||
const pv_tweak_nonce_t *nonce, enum PvCryptoMode mode,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(pv_crypto_ctx_t) ret = NULL;
|
||||
g_autoptr(BIO) xts_filter = NULL;
|
||||
|
||||
g_assert(input);
|
||||
STATIC_ASSERT(sizeof_field(pv_crypto_ctx_t, nonce) == sizeof(nonce));
|
||||
|
||||
ret = g_new0(pv_crypto_ctx_t, 1);
|
||||
xts_filter = pv_BIO_cipher_new(EVP_aes_256_xts(), key, key_size, mode, error);
|
||||
if (!xts_filter) {
|
||||
g_prefix_error(error,
|
||||
_("Initializing the zdump crypto context failed" ERR_NEWLINE));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (BIO_get_cipher_ctx(xts_filter, &ret->cipher_ctx) != 1) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CRYPTO_CTX,
|
||||
_("Initializing the zdump crypto context failed"));
|
||||
return NULL;
|
||||
}
|
||||
g_assert(ret->cipher_ctx);
|
||||
|
||||
/* set-up BIO chain for the encryption/decryption */
|
||||
ret->filter = BIO_push(g_steal_pointer(&xts_filter), input);
|
||||
g_assert(ret->filter);
|
||||
|
||||
if (BIO_up_ref(input) != 1) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CRYPTO_CTX,
|
||||
_("Initializing the zdump crypto context failed"));
|
||||
return NULL;
|
||||
}
|
||||
ret->input = input;
|
||||
|
||||
ret->nonce = OPENSSL_malloc(sizeof(*ret->nonce));
|
||||
if (!ret->nonce)
|
||||
abort();
|
||||
(void)memcpy(ret->nonce, nonce, sizeof(*ret->nonce));
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
void pv_crypto_ctx_free(pv_crypto_ctx_t *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
g_clear_pointer(&ctx->input, BIO_vfree);
|
||||
g_clear_pointer(&ctx->filter, BIO_vfree);
|
||||
/* It's intentional that we don't free @ctx->cipher_ctx since it's not
|
||||
* owned by us, but the BIO chain */
|
||||
ctx->cipher_ctx = NULL;
|
||||
OPENSSL_clear_free(ctx->nonce, sizeof(*ctx->nonce));
|
||||
g_free(ctx);
|
||||
}
|
||||
|
||||
void calculate_tweak(const pv_tweak_component_t *tweak, const pv_tweak_nonce_t *nonce,
|
||||
pv_tweak_t *out)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(out->value); i++)
|
||||
out->value[i] = tweak->value[i] | nonce->value[i];
|
||||
}
|
||||
|
||||
int pv_read_page(BIO *input, BIO *output, GError **error)
|
||||
{
|
||||
char data[PAGE_SIZE];
|
||||
int rc;
|
||||
|
||||
rc = BIO_read(input, data, ARRAY_SIZE(data));
|
||||
if (rc != ARRAY_SIZE(data)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO, _("BIO_read failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = BIO_write(output, data, ARRAY_SIZE(data));
|
||||
if (rc != ARRAY_SIZE(data)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_BIO, _("BIO_write failed"));
|
||||
return -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
unsigned long pv_page_state(const pv_tweak_component_t *comp)
|
||||
{
|
||||
unsigned long ret = PV_INVAL_PAGE_STATE;
|
||||
|
||||
if (comp->special.indicator != PV_SPECIAL_INDICATOR)
|
||||
return PV_ENCRYPTED_PAGE;
|
||||
|
||||
if (comp->special.flag_reserved1 || comp->special.flag_reserved2 ||
|
||||
comp->special.is_zero_page + comp->special.is_shared_page +
|
||||
comp->special.is_mapped_page <
|
||||
1)
|
||||
return PV_INVAL_PAGE_STATE;
|
||||
|
||||
if (comp->special.is_zero_page)
|
||||
ret |= PV_ZERO_PAGE;
|
||||
if (comp->special.is_shared_page)
|
||||
ret |= PV_SHARED_PAGE;
|
||||
if (comp->special.is_mapped_page)
|
||||
ret |= PV_MAPPED_PAGE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const unsigned char NULL_DATA[PV_CSS_PAGESIZE] = { 0x0 };
|
||||
|
||||
static bool pv_BIO_is_seekable(BIO *bio)
|
||||
{
|
||||
const int type = BIO_method_type(bio);
|
||||
|
||||
return type == BIO_TYPE_FD || type == BIO_TYPE_FILE;
|
||||
}
|
||||
|
||||
static int pv_BIO_seek(BIO *bio, long long offset)
|
||||
{
|
||||
/* The documentation of @BIO_seeks says @offset is a `int` but the
|
||||
* source code actually shows it's a long. Therefore add these
|
||||
* additional checks here to detect in case something changes in
|
||||
* OpenSSL.
|
||||
*/
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic error "-Wconversion"
|
||||
#pragma GCC diagnostic error "-Wsign-conversion"
|
||||
return BIO_seek(bio, offset);
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
static int update_tweak(pv_crypto_ctx_t *crypto_ctx, GError **error)
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx = crypto_ctx->cipher_ctx;
|
||||
u8 *tweak = crypto_ctx->tweak_scratch.value;
|
||||
|
||||
/* Check tweak length */
|
||||
if (EVP_CIPHER_CTX_iv_length(ctx) != ARRAY_SIZE(crypto_ctx->tweak_scratch.value)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_TWEAK,
|
||||
_("Tweak has wrong size"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set the new tweak IV */
|
||||
if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, tweak, -1) != 1) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_TWEAK,
|
||||
_("Initializing tweaks failed"));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t pv_process_pglist(pv_crypto_ctx_t *ctx, BIO *output,
|
||||
const pv_tweak_component_t *tweak_components, size_t tweak_components_len,
|
||||
long input_off, GError **error)
|
||||
{
|
||||
bool is_output_seekable = pv_BIO_is_seekable(output);
|
||||
long cur_in_off = input_off, cur_out_off = 0;
|
||||
ssize_t page_idx;
|
||||
int rc;
|
||||
|
||||
assert(ctx->input);
|
||||
assert(ctx->filter);
|
||||
assert(output);
|
||||
assert(tweak_components_len <= SSIZE_MAX);
|
||||
|
||||
/* See https://www.openssl.org/docs/man1.1.0/man3/BIO_seek.html */
|
||||
if (!pv_BIO_is_seekable(ctx->input)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PGLIST_BIO,
|
||||
_("Input source is not seekable"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pv_BIO_seek(ctx->input, cur_in_off) == -1) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PGLIST_BIO,
|
||||
_("BIO_seek failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tweak_components_len > LONG_MAX / PV_CSS_PAGESIZE) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PAGELIST_ELF_OFFSET_TOO_LARGE,
|
||||
_("Possible long overflow detected: Try to read %li pages"),
|
||||
tweak_components_len);
|
||||
}
|
||||
|
||||
for (page_idx = 0; page_idx < (ssize_t)tweak_components_len; page_idx++) {
|
||||
const pv_tweak_component_t *tweak_comp = &tweak_components[page_idx];
|
||||
unsigned long page_state;
|
||||
BIO *input = NULL;
|
||||
|
||||
g_assert_nonnull(tweak_comp);
|
||||
|
||||
page_state = pv_page_state(tweak_comp);
|
||||
if (page_state & PV_INVAL_PAGE_STATE) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PGLIST_BIO,
|
||||
_("Invalid page state"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* g_assert(page_state & PV_MAPPED_PAGE); */
|
||||
page_state &= ~PV_MAPPED_PAGE;
|
||||
if (page_state & PV_ZERO_PAGE) {
|
||||
/* Nothing to do here for BIO_FILE because a sparse file
|
||||
* is filled with zeros by default. Therefore we can
|
||||
* simply calculate the new output offset. For a
|
||||
* BIO_s_mem BIO_seek doesn't work therefore we've to
|
||||
* work around.
|
||||
*/
|
||||
|
||||
cur_out_off += PV_CSS_PAGESIZE;
|
||||
cur_in_off += PV_CSS_PAGESIZE;
|
||||
if (G_UNLIKELY(pv_BIO_seek(ctx->input, cur_in_off) == -1)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PGLIST_BIO,
|
||||
_("BIO_seek failed"));
|
||||
return -1;
|
||||
}
|
||||
if (is_output_seekable) {
|
||||
if (G_UNLIKELY(pv_BIO_seek(output, cur_out_off) == -1)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR,
|
||||
ZDUMP_ERR_PGLIST_BIO, _("BIO_seek failed"));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (G_UNLIKELY(
|
||||
BIO_write(output, NULL_DATA, ARRAY_SIZE(NULL_DATA)) !=
|
||||
ARRAY_SIZE(NULL_DATA))) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR,
|
||||
ZDUMP_ERR_PGLIST_BIO, _("BIO_write failed"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
g_assert(!(page_state & ~PV_ZERO_PAGE));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (page_state & PV_SHARED_PAGE) {
|
||||
/* shared pages are not encrypted */
|
||||
input = ctx->input;
|
||||
g_assert(!(page_state & ~PV_SHARED_PAGE));
|
||||
} else if (page_state & PV_ENCRYPTED_PAGE) {
|
||||
input = ctx->filter;
|
||||
calculate_tweak(tweak_comp, ctx->nonce, &ctx->tweak_scratch);
|
||||
|
||||
/* set new tweak */
|
||||
if (update_tweak(ctx, error) < 0)
|
||||
return -1;
|
||||
|
||||
g_assert(!(page_state & ~PV_ENCRYPTED_PAGE));
|
||||
} else {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PGLIST_INVAL_STATE,
|
||||
_("Invalid page state. page-idx: %#lx, state: %#lx"), page_idx,
|
||||
page_state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = pv_read_page(input, output, error);
|
||||
if (rc != PV_CSS_PAGESIZE)
|
||||
return -1;
|
||||
|
||||
/* adapt the offsets */
|
||||
cur_in_off += rc;
|
||||
cur_out_off += rc;
|
||||
}
|
||||
|
||||
return page_idx;
|
||||
}
|
||||
|
||||
bool pv_is_pv_elf(const Elf64_Shdr *shdrs, const unsigned int shnum, const char *shstrtab,
|
||||
const size_t shstrtab_size)
|
||||
{
|
||||
return find_elf_shdr_by_name(shdrs, shnum, shstrtab, shstrtab_size,
|
||||
PV_ELF_SECTION_NAME_COMPL) != NULL;
|
||||
}
|
||||
|
||||
struct _storage_state_mmap {
|
||||
void *first_page_ptr;
|
||||
size_t mapped_size;
|
||||
pv_tweak_component_t *tweak_components;
|
||||
size_t num_tweaks;
|
||||
gatomicrefcount ref_count;
|
||||
};
|
||||
|
||||
storage_state_mmap_t *storage_state_mmap_new(const int fd, const u64 offset, const u64 size,
|
||||
GError **error)
|
||||
{
|
||||
size_t tweak_components_cnt, start_addr, in_page_offset, mmapped_size;
|
||||
g_autoptr(storage_state_mmap_t) ret = NULL;
|
||||
int saved_errno = 0;
|
||||
u8 *ptr;
|
||||
|
||||
if (size == 0 || size % sizeof(pv_tweak_component_t) != 0) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_MMAP,
|
||||
_("StorageState MMAP: size (%#llx) not a multiple of (%#lx)"), size,
|
||||
sizeof(pv_tweak_component_t));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
start_addr = page_start_addr(page_index(offset), error);
|
||||
if (*error)
|
||||
return NULL;
|
||||
|
||||
tweak_components_cnt = size / sizeof(pv_tweak_component_t);
|
||||
in_page_offset = page_offset(offset);
|
||||
mmapped_size = size + in_page_offset;
|
||||
|
||||
if (start_addr > SSIZE_MAX) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_MMAP,
|
||||
_("StorageState MMAP: page start address is too large (%#lx)"),
|
||||
start_addr);
|
||||
return NULL;
|
||||
}
|
||||
ptr = mmap(NULL, mmapped_size, PROT_READ, MAP_POPULATE | MAP_PRIVATE, fd,
|
||||
(ssize_t)start_addr);
|
||||
saved_errno = errno;
|
||||
if (ptr == MAP_FAILED) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_MMAP, _("mmap failed: %s"),
|
||||
g_strerror(saved_errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = g_new0(typeof(*ret), 1);
|
||||
ret->first_page_ptr = ptr;
|
||||
ret->tweak_components = (pv_tweak_component_t *)(ptr + in_page_offset);
|
||||
ret->num_tweaks = tweak_components_cnt;
|
||||
ret->mapped_size = mmapped_size;
|
||||
g_atomic_ref_count_init(&ret->ref_count);
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
storage_state_mmap_t *storage_state_mmap_ref(storage_state_mmap_t *storage_state)
|
||||
{
|
||||
g_assert(storage_state);
|
||||
g_atomic_ref_count_inc(&storage_state->ref_count);
|
||||
return storage_state;
|
||||
}
|
||||
|
||||
void storage_state_mmap_unref(storage_state_mmap_t *storage_state)
|
||||
{
|
||||
if (!storage_state)
|
||||
return;
|
||||
if (storage_state->ref_count && !g_atomic_ref_count_dec(&storage_state->ref_count))
|
||||
return;
|
||||
if (storage_state->first_page_ptr) {
|
||||
int rc = munmap(storage_state->first_page_ptr, storage_state->mapped_size);
|
||||
if (rc != 0)
|
||||
util_log_print(UTIL_LOG_WARN, _("munmap has failed"));
|
||||
}
|
||||
g_free(storage_state);
|
||||
}
|
||||
|
||||
pv_elf_ctx_t *pv_elf_ctx_new(const int fd,
|
||||
const pv_dump_completion_confidential_area_v1_t *cpl_conf,
|
||||
storage_state_mmap_t *storage_state_data, const u64 elf_load_offset,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(pv_elf_ctx_t) ret = NULL;
|
||||
g_autoptr(BIO) input = NULL;
|
||||
|
||||
input = BIO_new_fd(fd, BIO_NOCLOSE);
|
||||
if (!input) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_CTX_BIO,
|
||||
_("cannot open file"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = g_new0(typeof(*ret), 1);
|
||||
ret->pv_ctx = pv_crypto_ctx_new(input, cpl_conf->key, sizeof(cpl_conf->key),
|
||||
&cpl_conf->nonce, PV_DECRYPT, error);
|
||||
if (!ret->pv_ctx)
|
||||
return NULL;
|
||||
ret->storage_state_data = storage_state_mmap_ref(storage_state_data);
|
||||
ret->elf_load_off = elf_load_offset;
|
||||
return g_steal_pointer(&ret);
|
||||
}
|
||||
|
||||
void pv_elf_ctx_free(pv_elf_ctx_t *p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
g_clear_pointer(&p->pv_ctx, pv_crypto_ctx_free);
|
||||
g_clear_pointer(&p->storage_state_data, storage_state_mmap_unref);
|
||||
g_clear_pointer(&p->output, BIO_vfree);
|
||||
g_free(p);
|
||||
}
|
||||
|
||||
const pv_tweak_component_t *pv_get_tweak_components(storage_state_mmap_t *storage_state_data,
|
||||
u64 page_idx, u64 page_cnt)
|
||||
{
|
||||
u64 last_page_idx;
|
||||
|
||||
g_assert_cmpuint(page_cnt, >=, 1);
|
||||
|
||||
if (!u64_checked_add(&last_page_idx, page_idx, page_cnt - 1))
|
||||
return NULL;
|
||||
if (last_page_idx >= storage_state_data->num_tweaks)
|
||||
return NULL;
|
||||
|
||||
return &storage_state_data->tweak_components[page_idx];
|
||||
}
|
||||
|
||||
/* Return version number if possible */
|
||||
static long completion_data_get_version(GBytes *cpl_data, GError **error)
|
||||
{
|
||||
size_t size;
|
||||
const uint32_t *version = g_bytes_get_data(cpl_data, &size);
|
||||
STATIC_ASSERT(offsetof(pv_dump_completion_aad_v1_t, version) == 0);
|
||||
|
||||
/* check whether we can dereference @version */
|
||||
if (sizeof(*version) > size) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_UNSUPP_COMPL_VER,
|
||||
_("Unsupported " PV_ELF_SECTION_NAME_COMPL " section size (%#lx)"),
|
||||
size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return *version;
|
||||
}
|
||||
|
||||
int pv_process_section_data(const int fd, GBytes *completion_sec, const u64 storage_state_offset,
|
||||
const size_t storage_state_size, GBytes *cck,
|
||||
pv_dump_completion_t **completion_decr, GBytes **dump_key,
|
||||
storage_state_mmap_t **storage_state, GError **error)
|
||||
{
|
||||
g_autoptr(pv_dump_completion_t) _completion_decr = NULL;
|
||||
g_autoptr(storage_state_mmap_t) _storage_state_data = NULL;
|
||||
g_autoptr(GBytes) _dump_key = NULL;
|
||||
g_assert(completion_sec);
|
||||
long version;
|
||||
|
||||
version = completion_data_get_version(completion_sec, error);
|
||||
if (version < 0)
|
||||
return -1;
|
||||
|
||||
switch (version) {
|
||||
case PV_COMPL_DATA_VERSION_1: {
|
||||
const pv_dump_completion_data_v1_t *ccd;
|
||||
size_t size;
|
||||
|
||||
ccd = g_bytes_get_data(completion_sec, &size);
|
||||
if (sizeof(*ccd) > size) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_COMPL_DATA,
|
||||
_("Corrupted completion configuration data"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ccd->aad.len != sizeof(*ccd)) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_CORRUPTED_COMPL_DATA,
|
||||
_("Incorrect completion configuration data length"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
_dump_key = pv_derive_dump_key_v1(ccd, cck, error);
|
||||
if (!_dump_key) {
|
||||
g_prefix_error(error, _("Unable to derive dump key: "));
|
||||
return -1;
|
||||
}
|
||||
|
||||
_completion_decr = (pv_dump_completion_t *)pv_decrypt_dump_completion_v1(
|
||||
ccd, _dump_key, error);
|
||||
if (!_completion_decr) {
|
||||
g_prefix_error(error,
|
||||
_("Unable to decrypt completion configuration data: "));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
g_set_error(
|
||||
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_UNSUPP_COMPL_VER,
|
||||
_("Unsupported dump completion version (%#lx) found in section " PV_ELF_SECTION_NAME_COMPL),
|
||||
version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
_storage_state_data =
|
||||
storage_state_mmap_new(fd, storage_state_offset, storage_state_size, error);
|
||||
if (!_storage_state_data)
|
||||
return -1;
|
||||
|
||||
*dump_key = g_steal_pointer(&_dump_key);
|
||||
*completion_decr = g_steal_pointer(&_completion_decr);
|
||||
*storage_state = g_steal_pointer(&_storage_state_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pv_elf_read(const pv_elf_ctx_t *elf_ctx, const u64 start_addr, void *dst, const u64 size,
|
||||
GError **error)
|
||||
{
|
||||
u64 pglist_size, pglist_start_idx, pglist_end_idx, pglist_num_pages;
|
||||
const pv_tweak_component_t *pglist_tweak_components;
|
||||
u64 pglist_start_addr, pglist_end_addr, pglist_elf_off, page_off;
|
||||
const unsigned char *data = NULL;
|
||||
g_autoptr(BIO) output = NULL;
|
||||
gssize num_processed_pages;
|
||||
long data_size;
|
||||
u64 end_addr;
|
||||
|
||||
/* nothing to do then */
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
end_addr = start_addr + size - 1;
|
||||
if (end_addr < start_addr) {
|
||||
g_set_error(
|
||||
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ_END_ADDR_OVERFLOW,
|
||||
_("UInt Overflow during reading memory detected. start_addr %p, size: %#llx"),
|
||||
(void *)start_addr, size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
page_off = page_offset(start_addr);
|
||||
pglist_start_idx = page_index(start_addr);
|
||||
pglist_start_addr = page_start_addr(pglist_start_idx, error);
|
||||
/* Could never happen here */
|
||||
if (*error)
|
||||
return -1;
|
||||
pglist_end_idx = page_index(end_addr);
|
||||
pglist_end_addr = page_end_addr(pglist_end_idx, error);
|
||||
if (*error)
|
||||
return -1;
|
||||
pglist_size = pglist_end_addr - pglist_start_addr + 1;
|
||||
pglist_num_pages = pglist_end_idx - pglist_start_idx + 1;
|
||||
|
||||
g_assert(IS_ALIGNED(pglist_start_addr, PV_CSS_PAGESIZE));
|
||||
g_assert(IS_ALIGNED(pglist_end_addr + 1, PV_CSS_PAGESIZE));
|
||||
g_assert(IS_ALIGNED(pglist_size, PV_CSS_PAGESIZE));
|
||||
/* must be true, is max UINT64_T/0x1000 + 1 < SSIZE_MAX */
|
||||
g_assert(pglist_num_pages <= SSIZE_MAX);
|
||||
g_assert(pglist_num_pages > 0);
|
||||
|
||||
pglist_tweak_components = pv_get_tweak_components(elf_ctx->storage_state_data,
|
||||
pglist_start_idx, pglist_num_pages);
|
||||
if (!pglist_tweak_components) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ,
|
||||
_("Page tweaks were not found idx %llu num %llu"), pglist_start_idx,
|
||||
pglist_num_pages);
|
||||
return -1;
|
||||
}
|
||||
|
||||
output = BIO_new(BIO_s_mem());
|
||||
if (!output)
|
||||
abort();
|
||||
|
||||
if (!u64_checked_add(&pglist_elf_off, elf_ctx->elf_load_off, pglist_start_addr)) {
|
||||
g_set_error(
|
||||
error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PAGELIST_ELF_OFFSET_TOO_LARGE,
|
||||
_("UInt overflow detected: ELF load offset %#llx page start address %#llx"),
|
||||
elf_ctx->elf_load_off, pglist_start_addr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pglist_elf_off > LONG_MAX) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_PAGELIST_ELF_OFFSET_TOO_LARGE,
|
||||
_("ELF load offset is too large"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Process the pages - in this case do the decryption */
|
||||
num_processed_pages = pv_process_pglist(elf_ctx->pv_ctx, output, pglist_tweak_components,
|
||||
pglist_num_pages, (long)pglist_elf_off, error);
|
||||
if (num_processed_pages < 0)
|
||||
return -1;
|
||||
|
||||
if (num_processed_pages != (gssize)pglist_num_pages) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ,
|
||||
_("Processed page count isn't correct"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (BIO_flush(output) != 1) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ, _("BIO_flush failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
data_size = BIO_get_mem_data(output, &data);
|
||||
if (data_size < 0 || !data) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ,
|
||||
_("BIO_get_mem_data failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((u64)data_size != pglist_size) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ,
|
||||
_("Decrypting memory failed"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* NOTE previous assertions/overflow checks assure that this can never happen.
|
||||
* We keep this to be extra sure and protect the following memcpy from
|
||||
* malicious copying.
|
||||
*/
|
||||
if (page_off + size > (u64)data_size) {
|
||||
g_set_error(error, ZDUMP_PV_UTILS_ERROR, ZDUMP_ERR_ELF_READ, _("%s: Illegal state"),
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)memcpy(dst, data + page_off, size);
|
||||
return 0;
|
||||
}
|
||||
347
zdump/pv_utils.h
Normal file
347
zdump/pv_utils.h
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Utilities to decrypt secure execution guest dumps.
|
||||
*
|
||||
* Copyright IBM Corp. 2022
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef PV_UTILS_H
|
||||
#define PV_UTILS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "lib/util_log.h"
|
||||
#include "libpv/crypto.h"
|
||||
|
||||
#include "df_elf.h"
|
||||
#include "dfi.h"
|
||||
#include "pv_defs.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define PV_INVAL_PAGE_STATE 0UL
|
||||
#define PV_ENCRYPTED_PAGE (1UL << 0)
|
||||
#define PV_SHARED_PAGE (1UL << 1)
|
||||
#define PV_ZERO_PAGE (1UL << 2)
|
||||
#define PV_MAPPED_PAGE (1UL << 3)
|
||||
|
||||
#define FUNC_NAME_FLAT_STRUCT_CLEANSE_FREE(type) type##_flat_struct_cleanse_free
|
||||
#define DEFINE_FLAT_STRUCT_CLEANSE_FREE(type, free_func) \
|
||||
static void FUNC_NAME_FLAT_STRUCT_CLEANSE_FREE(type)(type * obj) \
|
||||
{ \
|
||||
if (!obj) \
|
||||
return; \
|
||||
OPENSSL_cleanse(obj, sizeof(type)); \
|
||||
free_func(obj); \
|
||||
}
|
||||
|
||||
/* The typedef is required to be able to define a autoptr cleanup
|
||||
* function using glib2.
|
||||
*/
|
||||
typedef struct dfi_cpu dfi_cpu_t;
|
||||
|
||||
DEFINE_FLAT_STRUCT_CLEANSE_FREE(dfi_cpu_t, dfi_cpu_free)
|
||||
DEFINE_FLAT_STRUCT_CLEANSE_FREE(pv_dump_completion_data_v1_t, g_free)
|
||||
DEFINE_FLAT_STRUCT_CLEANSE_FREE(pv_dump_completion_confidential_area_v1_t, g_free)
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(dfi_cpu_t, FUNC_NAME_FLAT_STRUCT_CLEANSE_FREE(dfi_cpu_t))
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(
|
||||
pv_dump_completion_data_v1_t,
|
||||
FUNC_NAME_FLAT_STRUCT_CLEANSE_FREE(pv_dump_completion_data_v1_t))
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(
|
||||
pv_dump_completion_confidential_area_v1_t,
|
||||
FUNC_NAME_FLAT_STRUCT_CLEANSE_FREE(pv_dump_completion_confidential_area_v1_t))
|
||||
|
||||
typedef struct _pv_crypto_ctx pv_crypto_ctx_t;
|
||||
typedef struct _storage_state_mmap storage_state_mmap_t;
|
||||
struct _pv_elf_ctx {
|
||||
BIO *output;
|
||||
u64 elf_load_off;
|
||||
pv_crypto_ctx_t *pv_ctx;
|
||||
storage_state_mmap_t *storage_state_data;
|
||||
};
|
||||
typedef struct _pv_elf_ctx pv_elf_ctx_t;
|
||||
|
||||
void pv_dump_completion_free(pv_dump_completion_t *cpl);
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(pv_dump_completion_t, pv_dump_completion_free)
|
||||
|
||||
/**
|
||||
* pv_derive_dump_key_v1:
|
||||
* @cpl_data: v1 dump completion data
|
||||
* @cck: Customer communication key
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Derives an 32 byte AES-256-GCM key using the key derivation function HDKF
|
||||
* together with the customer communication key (CCK) @cck as input keying
|
||||
* material, and the seed from the completion configuration dump @cpl_data as salt.
|
||||
*
|
||||
* Returns: 32 byte AES-256-GCM key on success, %NULL if an error occurred
|
||||
*/
|
||||
GBytes *pv_derive_dump_key_v1(const pv_dump_completion_data_v1_t *cpl_data, GBytes *cck,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* pv_decrypt_dump_completion_v1:
|
||||
* @cpl_data: v1 dump completion data
|
||||
* @dump_key: Dump key
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Decrypts and authenticates the completion configuration dump data @cpl_data
|
||||
* using the AES-256-GCM @dump_key.
|
||||
*
|
||||
* Returns: Decrypted confidential area of the completion configuration dump on success,
|
||||
* %NULL if an error occurred
|
||||
*/
|
||||
pv_dump_completion_v1_t *pv_decrypt_dump_completion_v1(const pv_dump_completion_data_v1_t *cpl_data,
|
||||
GBytes *dump_key, GError **error);
|
||||
|
||||
dfi_cpu_t *pv_decrypt_cpu_note_data(const unsigned int expected_version, GBytes *cpu_note,
|
||||
GBytes *dump_key, GError **error);
|
||||
|
||||
/**
|
||||
* pv_decrypt_cpu_dump_area_v1:
|
||||
* @cpu_dump_area: a v1 CPU dump area
|
||||
* @dump_key: Dump key
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Decrypts and authenticates a secure CPU dump using AES-256-GCM with specified
|
||||
* dump_key.
|
||||
*
|
||||
* Returns: Decrypted part of the CPU dump on success, %NULL if an error occurred
|
||||
*/
|
||||
GBytes *pv_decrypt_cpu_dump_area_v1(const pv_cpu_dump_v1_t *cpu_dump_area, GBytes *dump_key,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* pv_dfi_cpu_from_pv_cpu:
|
||||
* @pv_cpu: PV CPU data
|
||||
*
|
||||
* Convert PV CPU data to internal dfi_cpu struct.
|
||||
*
|
||||
* Returns: #dfi_cpu struct
|
||||
*/
|
||||
dfi_cpu_t *pv_dfi_cpu_from_pv_cpu(const pv_cpu_dump_confidential_area_v1_t *pv_cpu);
|
||||
|
||||
/**
|
||||
* pv_crypto_ctx_new:
|
||||
* @input: a #BIO input
|
||||
* @key: (array length=key_size): byte data of the key
|
||||
* @key_size: size of @key in bytes
|
||||
* @nonce: The PV tweak nonce
|
||||
* @mode: encrypt or decrypt mode
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Create a PV crypto context using @input as the input, @key as the cipher key
|
||||
* and @nonce as the nonce for deriving the tweaks.
|
||||
*
|
||||
* Returns: a new #pv_crypto_ctx_t
|
||||
*/
|
||||
pv_crypto_ctx_t *pv_crypto_ctx_new(BIO *input, const unsigned char *key, size_t key_size,
|
||||
const pv_tweak_nonce_t *nonce, enum PvCryptoMode mode,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* pv_crypto_ctx_free:
|
||||
* @ctx: a #pv_crypto_ctx_t
|
||||
*
|
||||
* Free all resources and the memory allocated for the #pv_crypto_ctx_t
|
||||
*/
|
||||
void pv_crypto_ctx_free(pv_crypto_ctx_t *ctx);
|
||||
|
||||
/**
|
||||
* pv_process_pglist:
|
||||
* @ctx: a #pv_crypto_ctx_t context
|
||||
* @output: Output
|
||||
* @tweaks_components: (array length=tweaks_len) (element-type pv_tweak_component_t): tweaks
|
||||
* components to be used for the decryption
|
||||
* @tweaks_components_len: the length of @tweaks and the length of the page list to be processed
|
||||
* @input_off: the file offset of the encrypted memory passed in @ctx to decrypt
|
||||
* @error: return location for a #GError
|
||||
*
|
||||
* Decrypts a page block (e.g. 256 pages).
|
||||
*
|
||||
* Returns: number of processed pages on success, -1 if an error occurred
|
||||
*/
|
||||
ssize_t pv_process_pglist(pv_crypto_ctx_t *ctx, BIO *output,
|
||||
const pv_tweak_component_t *tweak_components, size_t tweak_components_len,
|
||||
long input_off, GError **error);
|
||||
|
||||
/**
|
||||
* calculate_tweak:
|
||||
*
|
||||
* calculates the bitwise or of tweak component and nonce.
|
||||
*/
|
||||
void calculate_tweak(const pv_tweak_component_t *tweak, const pv_tweak_nonce_t *nonce,
|
||||
pv_tweak_t *out);
|
||||
/**
|
||||
* pv_read_page:
|
||||
* @output: Input
|
||||
* @output: Output
|
||||
*
|
||||
* Read one page data from @input and write it to @output.
|
||||
*
|
||||
* Returns: The number of bytes read for success, -1 if an error occurred
|
||||
*/
|
||||
int pv_read_page(BIO *input, BIO *output, GError **error);
|
||||
|
||||
/**
|
||||
* pv_page_state:
|
||||
*
|
||||
* Get the page state of the tweak component.
|
||||
*/
|
||||
unsigned long pv_page_state(const pv_tweak_component_t *comp);
|
||||
|
||||
/**
|
||||
* pv_get_tweak_components:
|
||||
* @storage_state_data: input data
|
||||
* @page_idx: Page index of the fist page
|
||||
* @page_cnt: number of tweak components that can be at least accessed
|
||||
*
|
||||
* get a pointer to the page states of the given pages.
|
||||
*
|
||||
* Returns: pointer to page states or NULL in case of an error
|
||||
*
|
||||
*/
|
||||
const pv_tweak_component_t *pv_get_tweak_components(storage_state_mmap_t *storage_state_data,
|
||||
u64 page_idx, u64 page_cnt);
|
||||
|
||||
/**
|
||||
* pv_elf_ctx_new:
|
||||
* fd: file descriptor of elf file
|
||||
* @completion_confidential_decr: content of ection pv_meta size must be
|
||||
* sizeof(pv_dump_completion_confidential_area_t)
|
||||
* @storage_state_data: mmap to pv_mem_meta
|
||||
* @load_offset: offset of LOAD segment in fd
|
||||
* @error: return value for GError
|
||||
*
|
||||
* creates a new elf context for processing the elf file
|
||||
*
|
||||
* Returns: new elf context or NULL in case of error
|
||||
*/
|
||||
pv_elf_ctx_t *
|
||||
pv_elf_ctx_new(const int fd,
|
||||
const pv_dump_completion_confidential_area_v1_t *completion_confidential_decr,
|
||||
storage_state_mmap_t *storage_state_data, const u64 load_offset, GError **error);
|
||||
|
||||
/**
|
||||
* pv_info_free:
|
||||
* frees the elf context
|
||||
*/
|
||||
void pv_elf_ctx_free(pv_elf_ctx_t *p);
|
||||
|
||||
/**
|
||||
* pv_is_pv_elf:
|
||||
* @shdrs: list of section headers
|
||||
* @shnum: size of shdrs list
|
||||
* @shstrtab: content of shstrtab (section hdr string table)
|
||||
* @shstrtab_size: size of shstrtab
|
||||
*
|
||||
* Returns: true in case it is a PV ELF vmcore dump, false otherwise.
|
||||
*/
|
||||
bool pv_is_pv_elf(const Elf64_Shdr *shdrs, const unsigned int shnum, const char *shstrtab,
|
||||
const size_t shstrtab_size);
|
||||
|
||||
/**
|
||||
* pv_process_section_data:
|
||||
*
|
||||
* Uses the ELF sections data to derive dump key, using the provided
|
||||
* customer-communication-key. It copies the decrypted completion
|
||||
* data into @completion_decr and mmaps storage state tweaks to storage
|
||||
* state data.
|
||||
*
|
||||
* The caller is responsible for cleanup and overwriting.
|
||||
*
|
||||
* Returns: 0 in case of success, -1 otherwise.
|
||||
*/
|
||||
int pv_process_section_data(const int fd, GBytes *completion_sec, const u64 storage_state_offset,
|
||||
const size_t storage_state_size, GBytes *cck,
|
||||
pv_dump_completion_t **completion_decr, GBytes **dump_key,
|
||||
storage_state_mmap_t **storage_state_data, GError **error);
|
||||
|
||||
/**
|
||||
* pv_elf_read:
|
||||
*
|
||||
* elf_ctx: elf context
|
||||
* @start_addr: first address to read from
|
||||
* @dst: target
|
||||
* @size: num bytes to read
|
||||
* @error: return value for GError
|
||||
*
|
||||
* Read size bytes from start addr in elf_ctx and save it into dest.
|
||||
* Will decrypt pages if defined by page state.
|
||||
*
|
||||
* Returns: 0 in case of success, -1 otherwise.
|
||||
*/
|
||||
int pv_elf_read(const pv_elf_ctx_t *elf_ctx, const u64 start_addr, void *dst, const u64 size,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* storage_state_mmap_new:
|
||||
* @fd: file descriptor for which to create the mapping
|
||||
* @offset: offset in fd to pv_mem_meta section
|
||||
* @size: size of mapping (and pv_mem_meta section)
|
||||
* @error: return value for GError
|
||||
*
|
||||
* Returns: new storage_state_mmap context
|
||||
*/
|
||||
storage_state_mmap_t *storage_state_mmap_new(const int fd, const u64 offset, const u64 size,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* storage_state_ref:
|
||||
*
|
||||
* increase the reference counter by one.
|
||||
*
|
||||
* Returns: pointer to storage_state
|
||||
*/
|
||||
storage_state_mmap_t *storage_state_mmap_ref(storage_state_mmap_t *storage_state);
|
||||
|
||||
/**
|
||||
* storage_state_unref:
|
||||
*
|
||||
* Decreases the reference counter by one.
|
||||
* If the counter is now zero storage_state will be unmapped and freed.
|
||||
* If the munmap fails, an error will be logged.
|
||||
*/
|
||||
void storage_state_mmap_unref(storage_state_mmap_t *storage_state);
|
||||
|
||||
#define ZDUMP_PV_UTILS_ERROR g_quark_from_static_string("zdump-pv-utils")
|
||||
typedef enum {
|
||||
ZDUMP_ERR_BIO,
|
||||
ZDUMP_ERR_BIO_FAIL,
|
||||
ZDUMP_ERR_BIO_KEY,
|
||||
ZDUMP_ERR_CORRUPTED_COMPL_DATA,
|
||||
ZDUMP_ERR_CORRUPTED_NOTE,
|
||||
ZDUMP_ERR_CRYPTO_CTX,
|
||||
ZDUMP_ERR_ELF_CTX,
|
||||
ZDUMP_ERR_ELF_CTX_BIO,
|
||||
ZDUMP_ERR_ELF_READ,
|
||||
ZDUMP_ERR_ELF_READ_END_ADDR_OVERFLOW,
|
||||
ZDUMP_ERR_MMAP,
|
||||
ZDUMP_ERR_PAGELIST_ELF_OFFSET_TOO_LARGE,
|
||||
ZDUMP_ERR_PAGE_END_ADDR_OVERFLOW,
|
||||
ZDUMP_ERR_PAGE_START_ADDR_OVERFLOW,
|
||||
ZDUMP_ERR_PGLIST_BIO,
|
||||
ZDUMP_ERR_PGLIST_INVAL,
|
||||
ZDUMP_ERR_PGLIST_INVAL_STATE,
|
||||
ZDUMP_ERR_PROC_SECT,
|
||||
ZDUMP_ERR_TWEAK,
|
||||
ZDUMP_ERR_UNSUPP_COMPL_VER,
|
||||
ZDUMP_ERR_UNSUPP_SEC_CPU_VER,
|
||||
ZDUMP_ERR_WRONG_AAD_SIZE,
|
||||
ZDUMP_ERR_WRONG_CCK_SIZE,
|
||||
ZDUMP_ERR_WRONG_CONFIDENTIAL_SIZE,
|
||||
ZDUMP_ERR_WRONG_NOTE_SIZE,
|
||||
ZDUMP_ERR_WRONG_TAG_SIZE,
|
||||
} zdump_pv_utils_error_e;
|
||||
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(pv_crypto_ctx_t, pv_crypto_ctx_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(pv_elf_ctx_t, pv_elf_ctx_free)
|
||||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(storage_state_mmap_t, storage_state_mmap_unref)
|
||||
|
||||
#endif /* PV_UTILS_H */
|
||||
29
zdump/zg.c
29
zdump/zg.c
@@ -88,7 +88,7 @@ void __noreturn zg_exit(int rc)
|
||||
/*
|
||||
* Alloc memory and check for errors
|
||||
*/
|
||||
void *zg_alloc(unsigned int size)
|
||||
void *zg_alloc(size_t size)
|
||||
{
|
||||
void *ptr = calloc(size, 1);
|
||||
if (!ptr)
|
||||
@@ -99,7 +99,7 @@ void *zg_alloc(unsigned int size)
|
||||
/*
|
||||
* Realloc memory and check for errors
|
||||
*/
|
||||
void *zg_realloc(void *ptr, unsigned int size)
|
||||
void *zg_realloc(void *ptr, size_t size)
|
||||
{
|
||||
void *new_ptr = realloc(ptr, size);
|
||||
if (!new_ptr)
|
||||
@@ -130,7 +130,7 @@ void zg_free(void *ptr)
|
||||
/*
|
||||
* Return path name of open file
|
||||
*/
|
||||
const char *zg_path(struct zg_fh *zg_fh)
|
||||
const char *zg_path(const struct zg_fh *zg_fh)
|
||||
{
|
||||
return zg_fh->path;
|
||||
}
|
||||
@@ -138,7 +138,7 @@ const char *zg_path(struct zg_fh *zg_fh)
|
||||
/*
|
||||
* Return stat buffer of open file
|
||||
*/
|
||||
const struct stat *zg_stat(struct zg_fh *zg_fh)
|
||||
const struct stat *zg_stat(const struct zg_fh *zg_fh)
|
||||
{
|
||||
return &zg_fh->sb;
|
||||
}
|
||||
@@ -183,13 +183,14 @@ fail:
|
||||
void zg_close(struct zg_fh *zg_fh)
|
||||
{
|
||||
close(zg_fh->fh);
|
||||
free((void *)zg_fh->path);
|
||||
free(zg_fh);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read file
|
||||
*/
|
||||
ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check)
|
||||
ssize_t zg_read(const struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check)
|
||||
{
|
||||
size_t copied = 0;
|
||||
ssize_t rc;
|
||||
@@ -215,7 +216,7 @@ ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check)
|
||||
/*
|
||||
* Read line
|
||||
*/
|
||||
ssize_t zg_gets(struct zg_fh *zg_fh, void *ptr, size_t cnt, enum zg_check check)
|
||||
ssize_t zg_gets(const struct zg_fh *zg_fh, void *ptr, size_t cnt, enum zg_check check)
|
||||
{
|
||||
size_t copied = 0;
|
||||
char *buf = ptr;
|
||||
@@ -241,7 +242,7 @@ ssize_t zg_gets(struct zg_fh *zg_fh, void *ptr, size_t cnt, enum zg_check check)
|
||||
/*
|
||||
* Return file size
|
||||
*/
|
||||
u64 zg_size(struct zg_fh *zg_fh)
|
||||
u64 zg_size(const struct zg_fh *zg_fh)
|
||||
{
|
||||
return zg_fh->sb.st_size;
|
||||
}
|
||||
@@ -249,7 +250,7 @@ u64 zg_size(struct zg_fh *zg_fh)
|
||||
/*
|
||||
* Return file position
|
||||
*/
|
||||
off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check)
|
||||
off_t zg_tell(const struct zg_fh *zg_fh, enum zg_check check)
|
||||
{
|
||||
off_t rc;
|
||||
|
||||
@@ -263,7 +264,7 @@ off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check)
|
||||
/*
|
||||
* Seek to "off" relative to END
|
||||
*/
|
||||
off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
||||
off_t zg_seek_end(const struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
||||
{
|
||||
off_t rc;
|
||||
|
||||
@@ -276,7 +277,7 @@ off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
||||
/*
|
||||
* Seek to "off" in file
|
||||
*/
|
||||
off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
||||
off_t zg_seek(const struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
||||
{
|
||||
off_t rc;
|
||||
|
||||
@@ -294,7 +295,7 @@ off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
||||
/*
|
||||
* Seek from current position
|
||||
*/
|
||||
off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
||||
off_t zg_seek_cur(const struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
||||
{
|
||||
off_t rc;
|
||||
|
||||
@@ -307,7 +308,7 @@ off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
||||
/*
|
||||
* Do ioctl and exit in case of an error
|
||||
*/
|
||||
int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op,
|
||||
int zg_ioctl(const struct zg_fh *zg_fh, unsigned long rq, void *data, const char *op,
|
||||
enum zg_check check)
|
||||
{
|
||||
int rc;
|
||||
@@ -322,10 +323,10 @@ int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op,
|
||||
/*
|
||||
* Return file type
|
||||
*/
|
||||
enum zg_type zg_type(struct zg_fh *zg_fh)
|
||||
enum zg_type zg_type(const struct zg_fh *zg_fh)
|
||||
{
|
||||
struct mtop mtop;
|
||||
struct stat *sb = &zg_fh->sb;
|
||||
const struct stat *sb = &zg_fh->sb;
|
||||
|
||||
if (S_ISREG(sb->st_mode))
|
||||
return ZG_TYPE_FILE;
|
||||
|
||||
37
zdump/zg.h
37
zdump/zg.h
@@ -16,6 +16,7 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "boot/s390.h"
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
@@ -31,6 +33,8 @@
|
||||
#define U32_MAX ((u32) -1)
|
||||
#define U16_MAX ((u16) -1)
|
||||
#define U8_MAX ((u8) -1)
|
||||
#define OFF_T_MAX LONG_MAX
|
||||
STATIC_ASSERT(sizeof(off_t) == sizeof(long))
|
||||
|
||||
/*
|
||||
* IEC definitions
|
||||
@@ -45,8 +49,8 @@
|
||||
/*
|
||||
* Memory functions
|
||||
*/
|
||||
void *zg_alloc(unsigned int size);
|
||||
void *zg_realloc(void *ptr, unsigned int size);
|
||||
void *zg_alloc(size_t size);
|
||||
void *zg_realloc(void *ptr, size_t size);
|
||||
void zg_free(void *ptr);
|
||||
char *zg_strdup(const char *str);
|
||||
|
||||
@@ -81,6 +85,7 @@ void zg_abort(const char *fmt, ...);
|
||||
#define ERR_EXIT(fmt, ...) zg_err_exit(fmt, ## __VA_ARGS__)
|
||||
#define ERR_EXIT_ERRNO(fmt, ...) zg_err_exit_errno(fmt, ## __VA_ARGS__)
|
||||
#define ABORT(fmt, ...) zg_abort(fmt, ## __VA_ARGS__)
|
||||
#define ERR_NEWLINE "\n "
|
||||
|
||||
void zg_stderr(const char *fmt, ...);
|
||||
void zg_stderr_pr(const char *fmt, ...);
|
||||
@@ -92,9 +97,9 @@ void zg_stdout(const char *fmt, ...);
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
#define PAGE_SIZE 4096UL
|
||||
#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)
|
||||
#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
|
||||
#define IS_ALIGNED(addr, size) (!((addr) & (size - 1)))
|
||||
#define IS_PAGE_ALIGNED(addr) IS_ALIGNED((unsigned long)(addr), PAGE_SIZE)
|
||||
|
||||
static inline u32 zg_csum_partial(const void *buf, int len, u32 sum)
|
||||
{
|
||||
@@ -138,22 +143,20 @@ enum zg_check {
|
||||
ZG_CHECK_NONE,
|
||||
};
|
||||
|
||||
const char *zg_path(struct zg_fh *zg_fh);
|
||||
const struct stat *zg_stat(struct zg_fh *zg_fh);
|
||||
const char *zg_path(const struct zg_fh *zg_fh);
|
||||
const struct stat *zg_stat(const struct zg_fh *zg_fh);
|
||||
struct zg_fh *zg_open(const char *path, int flags, enum zg_check check);
|
||||
void zg_close(struct zg_fh *zg_fh);
|
||||
ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt,
|
||||
enum zg_check check);
|
||||
ssize_t zg_gets(struct zg_fh *zg_fh, void *buf, size_t cnt,
|
||||
enum zg_check check);
|
||||
u64 zg_size(struct zg_fh *zg_fh);
|
||||
off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check);
|
||||
off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op,
|
||||
ssize_t zg_read(const struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check);
|
||||
ssize_t zg_gets(const struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check);
|
||||
u64 zg_size(const struct zg_fh *zg_fh);
|
||||
off_t zg_tell(const struct zg_fh *zg_fh, enum zg_check check);
|
||||
off_t zg_seek(const struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
off_t zg_seek_end(const struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
off_t zg_seek_cur(const struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
||||
int zg_ioctl(const struct zg_fh *zg_fh, unsigned long rq, void *data, const char *op,
|
||||
enum zg_check check);
|
||||
enum zg_type zg_type(struct zg_fh *zg_fh);
|
||||
enum zg_type zg_type(const struct zg_fh *zg_fh);
|
||||
|
||||
/*
|
||||
* zgetdump actions
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
.\" Copyright 2018 IBM Corp.
|
||||
.\" Copyright 2022 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH ZGETDUMP 8 "April 2012" "s390-tools"
|
||||
.TH ZGETDUMP 8 "October 2022" "s390-tools"
|
||||
.SH NAME
|
||||
zgetdump \- Tool for copying and converting System z dumps
|
||||
.SH SYNOPSIS
|
||||
|
||||
\fBzgetdump\fR DUMP [-s SYS] [-f FMT] > DUMP_FILE
|
||||
\fBzgetdump\fR DUMP [-s SYS] [-f FMT] [-k KEY] > DUMP_FILE
|
||||
.br
|
||||
DUMP [-s SYS] [-f FMT] DUMP_FILE
|
||||
DUMP [-s SYS] [-f FMT] [-k KEY] DUMP_FILE
|
||||
.br
|
||||
-m DUMP [-s SYS] [-f FMT] DIR
|
||||
-m DUMP [-s SYS] [-f FMT] [-k KEY] DIR
|
||||
.br
|
||||
-i DUMP [-s SYS]
|
||||
-i DUMP [-s SYS] [-k KEY]
|
||||
.br
|
||||
-d DUMPDEV
|
||||
.br
|
||||
@@ -37,6 +37,10 @@ Print usage information, then exit.
|
||||
.BR "\-v" " or " "\-\-version"
|
||||
Print version information, then exit.
|
||||
|
||||
.TP
|
||||
.BR "\-k <KEY>" " or " "\-\-key <KEY>"
|
||||
Specify the file KEY to use as key for the decryption of a protected virtualization dump.
|
||||
|
||||
.TP
|
||||
.BR "\-m <DUMP> <DIR>" " or " "\-\-mount <DUMP> <DIR>"
|
||||
Mount the source dump DUMP to mount point DIR and generate a virtual target
|
||||
@@ -73,7 +77,7 @@ Use the specified target dump format FMT when writing or mounting the dump.
|
||||
The following target dump formats are supported:
|
||||
|
||||
.BR "- elf:"
|
||||
Executable and Linking Format core dump (default)
|
||||
Executable and Linkable Format core dump (default)
|
||||
|
||||
.BR "- s390:"
|
||||
s390 dump
|
||||
@@ -115,6 +119,8 @@ NVMe partition device node (e.g. /dev/nvme0n1p1)
|
||||
Tape device node (e.g. /dev/ntibm0)
|
||||
.IP " -" 12
|
||||
Device node for live system (/dev/mem or /dev/crash)
|
||||
.IP " -" 12
|
||||
Protected virtualization dump file (e.g. /dumps/pv.elf)
|
||||
.PP
|
||||
.IP " " 8
|
||||
Note: For DASD multi-volume dump it is sufficient to specify only one of the
|
||||
@@ -173,9 +179,12 @@ change the target format. The following dump formats are supported for
|
||||
target and source dump:
|
||||
.TP
|
||||
.BR "elf"
|
||||
Executable and Linking Format core dump. This dump format is also used for
|
||||
Executable and Linkable Format core dump. This dump format is also used for
|
||||
Linux user space core dumps.
|
||||
.TP
|
||||
.BR "ELF (protected virtualization dump):"
|
||||
Protected virtualization vmcore dump in Executable and Linkable Format
|
||||
.TP
|
||||
.BR "s390"
|
||||
This dump format is System z specific and is used for DASD and tape dumps.
|
||||
.TP
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "opts.h"
|
||||
#include "zgetdump.h"
|
||||
#include "dt.h"
|
||||
#include "dfi.h"
|
||||
@@ -105,6 +107,20 @@ static void kdump_select_check(void)
|
||||
ERR_EXIT("%s", msg);
|
||||
}
|
||||
|
||||
static int dfi_init_error(int rc)
|
||||
{
|
||||
if (rc == -ENOKEY) {
|
||||
if (g.opts.key_path)
|
||||
ERR("Cannot read key: '%s'", g.opts.key_path);
|
||||
else
|
||||
ERR("No key for dump decryption provided.");
|
||||
opts_print_usage(g.opts.prog_name);
|
||||
} else {
|
||||
STDERR("Dump cannot be processed (is not complete)\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run "--umount" action
|
||||
*/
|
||||
@@ -129,10 +145,13 @@ static int do_device_info(void)
|
||||
*/
|
||||
static int do_dump_info(void)
|
||||
{
|
||||
if (dfi_init() != 0) {
|
||||
int rc;
|
||||
|
||||
rc = dfi_init();
|
||||
if (rc != 0) {
|
||||
dfi_info_print();
|
||||
STDERR("\nERROR: Dump is not complete\n");
|
||||
zg_exit(1);
|
||||
STDERR("\n");
|
||||
return dfi_init_error(rc);
|
||||
}
|
||||
kdump_select_check();
|
||||
dfi_info_print();
|
||||
@@ -147,8 +166,9 @@ static int do_mount(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (dfi_init() != 0)
|
||||
ERR_EXIT("Dump cannot be processed (is not complete)");
|
||||
rc = dfi_init();
|
||||
if (rc != 0)
|
||||
return dfi_init_error(rc);
|
||||
dfo_init();
|
||||
kdump_select_check();
|
||||
rc = zfuse_mount_dump();
|
||||
@@ -189,8 +209,9 @@ static int do_copy(const char *output)
|
||||
FILE *stream;
|
||||
int rc;
|
||||
|
||||
if (dfi_init() != 0)
|
||||
ERR_EXIT("Dump cannot be processed (is not complete)");
|
||||
rc = dfi_init();
|
||||
if (rc != 0)
|
||||
return dfi_init_error(rc);
|
||||
dfo_init();
|
||||
kdump_select_check();
|
||||
stream = open_file_for_writing(output);
|
||||
|
||||
@@ -106,7 +106,7 @@ stage3.bin: stage3.exec
|
||||
--only-section=.stage2dump.tail \
|
||||
--only-section=.eckd2dump_mv.tail \
|
||||
--only-section=.fixup \
|
||||
--pad-to=0xe000 \
|
||||
--only-section=.sb.trailer \
|
||||
$< $@
|
||||
|
||||
data.o: $(FILES)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
#include "boot/loaders_layout.h"
|
||||
#include "boot/s390.h"
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
@@ -46,6 +47,15 @@ SECTIONS
|
||||
.rodata : {*(.rodata) }
|
||||
.data : { *(.data) }
|
||||
|
||||
/* Trailer needed for Secure Boot */
|
||||
. = COMMAND_LINE_EXTRA - 32;
|
||||
.sb.trailer : {
|
||||
QUAD(0x0000c00000000000)
|
||||
QUAD(STAGE3_ENTRY + PSW_LOAD)
|
||||
QUAD(STAGE3_ENTRY)
|
||||
QUAD(0x000000207a49504c)
|
||||
}
|
||||
|
||||
. = COMMAND_LINE_EXTRA;
|
||||
.cmdline_extra : {
|
||||
. += COMMAND_LINE_EXTRA_SIZE;
|
||||
|
||||
@@ -54,6 +54,5 @@ void misc_ebcdic_to_ascii(unsigned char *from, unsigned char *to);
|
||||
void misc_ascii_to_ebcdic(unsigned char *from, unsigned char *to);
|
||||
unsigned int misc_check_secure_boot(void);
|
||||
|
||||
#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
|
||||
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
||||
#endif /* not MISC_H */
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
/* Pointer to dedicated empty block in bootmap. */
|
||||
static disk_blockptr_t empty_block;
|
||||
|
||||
/* State of secure boot in the system */
|
||||
static bool secure_boot_supported;
|
||||
|
||||
/* Get size of a bootmap block pointer for disk with given INFO. */
|
||||
static int
|
||||
get_blockptr_size(struct disk_info* info)
|
||||
@@ -128,16 +131,27 @@ check_secure_boot_support(void)
|
||||
unsigned int val;
|
||||
FILE *fp;
|
||||
|
||||
if (verbose)
|
||||
printf("Secure boot support: ");
|
||||
|
||||
fp = fopen(ZIPL_SIPL_PATH, "r");
|
||||
if (!fp)
|
||||
if (!fp) {
|
||||
if (verbose)
|
||||
printf("not available\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fscanf(fp, "%d", &val) != 1) {
|
||||
if (verbose)
|
||||
printf("error\n");
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
if (verbose)
|
||||
printf("%s\n", val ? "yes" : "no");
|
||||
|
||||
return val ? true : false;
|
||||
}
|
||||
|
||||
@@ -508,7 +522,6 @@ static int add_ipl_program(int fd, char *filename,
|
||||
struct component_loc comp_loc[10];
|
||||
struct signature_header sig_head;
|
||||
size_t ramdisk_size, image_size;
|
||||
bool secure_boot_supported;
|
||||
size_t stage3_params_size;
|
||||
const char *comp_name[11];
|
||||
size_t signature_size;
|
||||
@@ -597,7 +610,6 @@ static int add_ipl_program(int fd, char *filename,
|
||||
return -1;
|
||||
}
|
||||
image_size = stats.st_size;
|
||||
secure_boot_supported = check_secure_boot_support();
|
||||
signature_size = extract_signature(ZIPL_STAGE3_PATH, &signature,
|
||||
&sig_head);
|
||||
if (signature_size &&
|
||||
@@ -1637,6 +1649,8 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table,
|
||||
disk_blockptr_t **stage1b_list, blocknum_t *stage1b_count,
|
||||
char **new_device, struct disk_info **new_info)
|
||||
{
|
||||
secure_boot_supported = check_secure_boot_support();
|
||||
|
||||
if (job->id == job_dump_partition) {
|
||||
if (is_ngdump_enabled(job->data.dump.device, &job->target))
|
||||
return bootmap_create_device_ngdump(job, program_table,
|
||||
|
||||
@@ -611,9 +611,6 @@ static struct util_list *get_multipath_status(const char *devname)
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
pclose(fp);
|
||||
|
||||
len = util_list_len(status);
|
||||
if (len == 0) {
|
||||
ERR("No paths found for '%s'\n", devname);
|
||||
@@ -625,15 +622,14 @@ static struct util_list *get_multipath_status(const char *devname)
|
||||
WARN("There are one or more failed paths for device '%s'\n",
|
||||
devname);
|
||||
}
|
||||
|
||||
return status;
|
||||
|
||||
out:
|
||||
goto success;
|
||||
out:
|
||||
status_list_free(status);
|
||||
status = NULL;
|
||||
success:
|
||||
free(line);
|
||||
pclose(fp);
|
||||
status_list_free(status);
|
||||
|
||||
return NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct util_list *get_multipath_data(const char *devname, char *args)
|
||||
|
||||
Reference in New Issue
Block a user