Files
s390-tools/zdump/zgetdump.c
Marc Hartmayer 8fa1b5a00b zdump: dfi: add support to read Protected Virtualization dumps
Sometimes dumping a virtual machine from the outside is the only way to
get the data that is needed. This can be the case if a dumping mechanism
like kdump hasn't been configured or data needs to be fetched at a
specific point. Dumping a protected guest from the outside without help
from FW/HW doesn't yield sufficient data to be useful. Hence we have
introduced Protected Virtualization (PV) dump support - also named
confidential dump support.

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

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

Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2022-11-03 17:45:38 +01:00

249 lines
4.6 KiB
C

/*
* zgetdump - Tool for copying and converting System z dumps
*
* Main functions
*
* Copyright IBM Corp. 2001, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/fs.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mtio.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "lib/zt_common.h"
#include "opts.h"
#include "zgetdump.h"
#include "dt.h"
#include "dfi.h"
#include "dfo.h"
#include "output.h"
#include "zfuse.h"
/*
* Globals
*/
struct zgetdump_globals g;
/*
* Signal handler for exiting zgetdump (the atexit handler will do the work)
*/
static void sig_exit(int sig)
{
(void) sig;
STDERR("\n"); /* E.g. to get newline after '^C' */
ERR_EXIT("Got signal %i, exiting...", sig);
}
/*
* Install signal handler
*/
static void sig_handler_init(void)
{
struct sigaction sigact = { 0 };
/* Ignore signals SIGUSR1 and SIGUSR2 */
if (sigemptyset(&sigact.sa_mask) < 0)
goto fail;
sigact.sa_handler = SIG_IGN;
if (sigaction(SIGUSR1, &sigact, NULL) < 0)
goto fail;
if (sigaction(SIGUSR2, &sigact, NULL) < 0)
goto fail;
/* Exit on SIGINT, SIGTERM, SIGHUP, ... */
if (sigemptyset(&sigact.sa_mask) < 0)
goto fail;
sigact.sa_handler = sig_exit;
if (sigaction(SIGINT, &sigact, NULL) < 0)
goto fail;
if (sigaction(SIGTERM, &sigact, NULL) < 0)
goto fail;
if (sigaction(SIGHUP, &sigact, NULL) < 0)
goto fail;
if (sigaction(SIGQUIT, &sigact, NULL) < 0)
goto fail;
if (sigaction(SIGALRM, &sigact, NULL) < 0)
goto fail;
if (sigaction(SIGPIPE, &sigact, NULL) < 0)
goto fail;
return;
fail:
ERR_EXIT_ERRNO("Could not initialize signal handler");
}
/*
* Check if dump contains kdump dump and production system dump
*/
static void kdump_select_check(void)
{
static char *msg =
"The dump contains \"kdump\" and \"production system\"\n"
" Access \"production system\" with \"-s prod\"\n"
" Access \"kdump\" with \"-s kdump\"\n"
" Access the complete dump with \"-s all\"\n"
" Send both dumps to your service organization";
if (g.opts.select_specified)
return;
if (!dfi_kdump_base())
return;
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
*/
static int do_umount(void)
{
zfuse_umount();
return 0;
}
/*
* Run "--device" action
*/
static int do_device_info(void)
{
dt_init();
dt_info_print();
return 0;
}
/*
* Run "--info" action
*/
static int do_dump_info(void)
{
int rc;
rc = dfi_init();
if (rc != 0) {
dfi_info_print();
STDERR("\n");
return dfi_init_error(rc);
}
kdump_select_check();
dfi_info_print();
dfi_exit();
return 0;
}
/*
* Run "--mount" action
*/
static int do_mount(void)
{
int rc;
rc = dfi_init();
if (rc != 0)
return dfi_init_error(rc);
dfo_init();
kdump_select_check();
rc = zfuse_mount_dump();
dfi_exit();
return rc;
}
static FILE *open_file_for_writing(const char *output)
{
FILE *stream;
int fd;
if (output) {
fd = open(output,
O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC,
0600);
if (fd == -1)
ERR_EXIT_ERRNO("Could not open '%s' exclusively", output);
} else {
fd = dup(STDOUT_FILENO);
if (fd == -1)
ERR_EXIT_ERRNO("Could not dup() stdout");
}
stream = fdopen(fd, "w");
if (!stream)
ERR_EXIT_ERRNO("Could not fdopen()");
fd = -1;
return stream;
}
/*
* Run "copy to output" action
*/
static int do_copy(const char *output)
{
FILE *stream;
int rc;
rc = dfi_init();
if (rc != 0)
return dfi_init_error(rc);
dfo_init();
kdump_select_check();
stream = open_file_for_writing(output);
rc = write_dump(stream);
fclose(stream);
dfi_exit();
return rc;
}
/*
* The zgetdump main function
*/
int main(int argc, char *argv[])
{
sig_handler_init();
opts_parse(argc, argv, &g.opts);
if (dfo_set(g.opts.fmt) != 0)
ERR_EXIT("Invalid target format \"%s\" specified", g.opts.fmt);
switch (g.opts.action) {
case ZG_ACTION_COPY:
return do_copy(g.opts.output_path);
case ZG_ACTION_DUMP_INFO:
return do_dump_info();
case ZG_ACTION_DEVICE_INFO:
return do_device_info();
case ZG_ACTION_MOUNT:
return do_mount();
case ZG_ACTION_UMOUNT:
return do_umount();
}
ABORT("Invalid action: %i", g.opts.action);
}