97168e
From 86aeb4fd7ff9395afba574e422d83f990ce1f047 Mon Sep 17 00:00:00 2001
97168e
From: Janosch Frank <frankja@linux.ibm.com>
97168e
Date: Mon, 17 Oct 2022 08:38:22 +0000
97168e
Subject: [PATCH 41/42] s390x: pv: Add dump support
97168e
MIME-Version: 1.0
97168e
Content-Type: text/plain; charset=UTF-8
97168e
Content-Transfer-Encoding: 8bit
97168e
97168e
RH-Author: Cédric Le Goater <clg@redhat.com>
97168e
RH-MergeRequest: 226: s390: Enhanced Interpretation for PCI Functions and Secure Execution guest dump
97168e
RH-Bugzilla: 1664378 2043909
97168e
RH-Acked-by: Thomas Huth <thuth@redhat.com>
97168e
RH-Acked-by: Cornelia Huck <cohuck@redhat.com>
97168e
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
97168e
RH-Commit: [41/41] 2731c2329276e76013e3b3df21e9743bc74edd2b
97168e
97168e
Sometimes dumping a guest from the outside is the only way to get the
97168e
data that is needed. This can be the case if a dumping mechanism like
97168e
KDUMP hasn't been configured or data needs to be fetched at a specific
97168e
point. Dumping a protected guest from the outside without help from
97168e
fw/hw doesn't yield sufficient data to be useful. Hence we now
97168e
introduce PV dump support.
97168e
97168e
The PV dump support works by integrating the firmware into the dump
97168e
process. New Ultravisor calls are used to initiate the dump process,
97168e
dump cpu data, dump memory state and lastly complete the dump process.
97168e
The UV calls are exposed by KVM via the new KVM_PV_DUMP command and
97168e
its subcommands. The guest's data is fully encrypted and can only be
97168e
decrypted by the entity that owns the customer communication key for
97168e
the dumped guest. Also dumping needs to be allowed via a flag in the
97168e
SE header.
97168e
97168e
On the QEMU side of things we store the PV dump data in the newly
97168e
introduced architecture ELF sections (storage state and completion
97168e
data) and the cpu notes (for cpu dump data).
97168e
97168e
Users can use the zgetdump tool to convert the encrypted QEMU dump to an
97168e
unencrypted one.
97168e
97168e
Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
97168e
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
97168e
Message-Id: <20221017083822.43118-11-frankja@linux.ibm.com>
97168e
(cherry picked from commit 113d8f4e95cf0450bea421263de6ec016c779ad0)
97168e
Signed-off-by: Cédric Le Goater <clg@redhat.com>
97168e
---
97168e
 dump/dump.c              |  12 +-
97168e
 include/sysemu/dump.h    |   5 +
97168e
 target/s390x/arch_dump.c | 262 +++++++++++++++++++++++++++++++++++----
97168e
 3 files changed, 246 insertions(+), 33 deletions(-)
97168e
97168e
diff --git a/dump/dump.c b/dump/dump.c
97168e
index 4aa8fb64d2..5dee060b73 100644
97168e
--- a/dump/dump.c
97168e
+++ b/dump/dump.c
97168e
@@ -709,9 +709,9 @@ static void dump_begin(DumpState *s, Error **errp)
97168e
     write_elf_notes(s, errp);
97168e
 }
97168e
 
97168e
-static int64_t dump_filtered_memblock_size(GuestPhysBlock *block,
97168e
-                                           int64_t filter_area_start,
97168e
-                                           int64_t filter_area_length)
97168e
+int64_t dump_filtered_memblock_size(GuestPhysBlock *block,
97168e
+                                    int64_t filter_area_start,
97168e
+                                    int64_t filter_area_length)
97168e
 {
97168e
     int64_t size, left, right;
97168e
 
97168e
@@ -729,9 +729,9 @@ static int64_t dump_filtered_memblock_size(GuestPhysBlock *block,
97168e
     return size;
97168e
 }
97168e
 
97168e
-static int64_t dump_filtered_memblock_start(GuestPhysBlock *block,
97168e
-                                            int64_t filter_area_start,
97168e
-                                            int64_t filter_area_length)
97168e
+int64_t dump_filtered_memblock_start(GuestPhysBlock *block,
97168e
+                                     int64_t filter_area_start,
97168e
+                                     int64_t filter_area_length)
97168e
 {
97168e
     if (filter_area_length) {
97168e
         /* return -1 if the block is not within filter area */
97168e
diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h
97168e
index 38ccac7190..4ffed0b659 100644
97168e
--- a/include/sysemu/dump.h
97168e
+++ b/include/sysemu/dump.h
97168e
@@ -215,4 +215,9 @@ typedef struct DumpState {
97168e
 uint16_t cpu_to_dump16(DumpState *s, uint16_t val);
97168e
 uint32_t cpu_to_dump32(DumpState *s, uint32_t val);
97168e
 uint64_t cpu_to_dump64(DumpState *s, uint64_t val);
97168e
+
97168e
+int64_t dump_filtered_memblock_size(GuestPhysBlock *block, int64_t filter_area_start,
97168e
+                                    int64_t filter_area_length);
97168e
+int64_t dump_filtered_memblock_start(GuestPhysBlock *block, int64_t filter_area_start,
97168e
+                                     int64_t filter_area_length);
97168e
 #endif
97168e
diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c
97168e
index f60a14920d..a2329141e8 100644
97168e
--- a/target/s390x/arch_dump.c
97168e
+++ b/target/s390x/arch_dump.c
97168e
@@ -12,11 +12,13 @@
97168e
  */
97168e
 
97168e
 #include "qemu/osdep.h"
97168e
+#include "qemu/units.h"
97168e
 #include "cpu.h"
97168e
 #include "s390x-internal.h"
97168e
 #include "elf.h"
97168e
 #include "sysemu/dump.h"
97168e
-
97168e
+#include "hw/s390x/pv.h"
97168e
+#include "kvm/kvm_s390x.h"
97168e
 
97168e
 struct S390xUserRegsStruct {
97168e
     uint64_t psw[2];
97168e
@@ -76,9 +78,16 @@ typedef struct noteStruct {
97168e
         uint64_t todcmp;
97168e
         uint32_t todpreg;
97168e
         uint64_t ctrs[16];
97168e
+        uint8_t dynamic[1];  /*
97168e
+                              * Would be a flexible array member, if
97168e
+                              * that was legal inside a union. Real
97168e
+                              * size comes from PV info interface.
97168e
+                              */
97168e
     } contents;
97168e
 } QEMU_PACKED Note;
97168e
 
97168e
+static bool pv_dump_initialized;
97168e
+
97168e
 static void s390x_write_elf64_prstatus(Note *note, S390CPU *cpu, int id)
97168e
 {
97168e
     int i;
97168e
@@ -177,28 +186,39 @@ static void s390x_write_elf64_prefix(Note *note, S390CPU *cpu, int id)
97168e
     note->contents.prefix = cpu_to_be32((uint32_t)(cpu->env.psa));
97168e
 }
97168e
 
97168e
+static void s390x_write_elf64_pv(Note *note, S390CPU *cpu, int id)
97168e
+{
97168e
+    note->hdr.n_type = cpu_to_be32(NT_S390_PV_CPU_DATA);
97168e
+    if (!pv_dump_initialized) {
97168e
+        return;
97168e
+    }
97168e
+    kvm_s390_dump_cpu(cpu, &note->contents.dynamic);
97168e
+}
97168e
 
97168e
 typedef struct NoteFuncDescStruct {
97168e
     int contents_size;
97168e
+    uint64_t (*note_size_func)(void); /* NULL for non-dynamic sized contents */
97168e
     void (*note_contents_func)(Note *note, S390CPU *cpu, int id);
97168e
+    bool pvonly;
97168e
 } NoteFuncDesc;
97168e
 
97168e
 static const NoteFuncDesc note_core[] = {
97168e
-    {sizeof_field(Note, contents.prstatus), s390x_write_elf64_prstatus},
97168e
-    {sizeof_field(Note, contents.fpregset), s390x_write_elf64_fpregset},
97168e
-    { 0, NULL}
97168e
+    {sizeof_field(Note, contents.prstatus), NULL, s390x_write_elf64_prstatus, false},
97168e
+    {sizeof_field(Note, contents.fpregset), NULL, s390x_write_elf64_fpregset, false},
97168e
+    { 0, NULL, NULL, false}
97168e
 };
97168e
 
97168e
 static const NoteFuncDesc note_linux[] = {
97168e
-    {sizeof_field(Note, contents.prefix),   s390x_write_elf64_prefix},
97168e
-    {sizeof_field(Note, contents.ctrs),     s390x_write_elf64_ctrs},
97168e
-    {sizeof_field(Note, contents.timer),    s390x_write_elf64_timer},
97168e
-    {sizeof_field(Note, contents.todcmp),   s390x_write_elf64_todcmp},
97168e
-    {sizeof_field(Note, contents.todpreg),  s390x_write_elf64_todpreg},
97168e
-    {sizeof_field(Note, contents.vregslo),  s390x_write_elf64_vregslo},
97168e
-    {sizeof_field(Note, contents.vregshi),  s390x_write_elf64_vregshi},
97168e
-    {sizeof_field(Note, contents.gscb),     s390x_write_elf64_gscb},
97168e
-    { 0, NULL}
97168e
+    {sizeof_field(Note, contents.prefix),   NULL, s390x_write_elf64_prefix,  false},
97168e
+    {sizeof_field(Note, contents.ctrs),     NULL, s390x_write_elf64_ctrs,    false},
97168e
+    {sizeof_field(Note, contents.timer),    NULL, s390x_write_elf64_timer,   false},
97168e
+    {sizeof_field(Note, contents.todcmp),   NULL, s390x_write_elf64_todcmp,  false},
97168e
+    {sizeof_field(Note, contents.todpreg),  NULL, s390x_write_elf64_todpreg, false},
97168e
+    {sizeof_field(Note, contents.vregslo),  NULL, s390x_write_elf64_vregslo, false},
97168e
+    {sizeof_field(Note, contents.vregshi),  NULL, s390x_write_elf64_vregshi, false},
97168e
+    {sizeof_field(Note, contents.gscb),     NULL, s390x_write_elf64_gscb,    false},
97168e
+    {0, kvm_s390_pv_dmp_get_size_cpu,       s390x_write_elf64_pv, true},
97168e
+    { 0, NULL, NULL, false}
97168e
 };
97168e
 
97168e
 static int s390x_write_elf64_notes(const char *note_name,
97168e
@@ -207,22 +227,41 @@ static int s390x_write_elf64_notes(const char *note_name,
97168e
                                        DumpState *s,
97168e
                                        const NoteFuncDesc *funcs)
97168e
 {
97168e
-    Note note;
97168e
+    Note note, *notep;
97168e
     const NoteFuncDesc *nf;
97168e
-    int note_size;
97168e
+    int note_size, content_size;
97168e
     int ret = -1;
97168e
 
97168e
     assert(strlen(note_name) < sizeof(note.name));
97168e
 
97168e
     for (nf = funcs; nf->note_contents_func; nf++) {
97168e
-        memset(&note, 0, sizeof(note));
97168e
-        note.hdr.n_namesz = cpu_to_be32(strlen(note_name) + 1);
97168e
-        note.hdr.n_descsz = cpu_to_be32(nf->contents_size);
97168e
-        g_strlcpy(note.name, note_name, sizeof(note.name));
97168e
-        (*nf->note_contents_func)(&note, cpu, id);
97168e
+        notep = ¬e;
97168e
+        if (nf->pvonly && !s390_is_pv()) {
97168e
+            continue;
97168e
+        }
97168e
+
97168e
+        content_size = nf->note_size_func ? nf->note_size_func() : nf->contents_size;
97168e
+        note_size = sizeof(note) - sizeof(notep->contents) + content_size;
97168e
+
97168e
+        /* Notes with dynamic sizes need to allocate a note */
97168e
+        if (nf->note_size_func) {
97168e
+            notep = g_malloc(note_size);
97168e
+        }
97168e
+
97168e
+        memset(notep, 0, sizeof(note));
97168e
 
97168e
-        note_size = sizeof(note) - sizeof(note.contents) + nf->contents_size;
97168e
-        ret = f(&note, note_size, s);
97168e
+        /* Setup note header data */
97168e
+        notep->hdr.n_descsz = cpu_to_be32(content_size);
97168e
+        notep->hdr.n_namesz = cpu_to_be32(strlen(note_name) + 1);
97168e
+        g_strlcpy(notep->name, note_name, sizeof(notep->name));
97168e
+
97168e
+        /* Get contents and write them out */
97168e
+        (*nf->note_contents_func)(notep, cpu, id);
97168e
+        ret = f(notep, note_size, s);
97168e
+
97168e
+        if (nf->note_size_func) {
97168e
+            g_free(notep);
97168e
+        }
97168e
 
97168e
         if (ret < 0) {
97168e
             return -1;
97168e
@@ -247,13 +286,179 @@ int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
97168e
     return s390x_write_elf64_notes("LINUX", f, cpu, cpuid, s, note_linux);
97168e
 }
97168e
 
97168e
+/* PV dump section size functions */
97168e
+static uint64_t get_mem_state_size_from_len(uint64_t len)
97168e
+{
97168e
+    return (len / (MiB)) * kvm_s390_pv_dmp_get_size_mem_state();
97168e
+}
97168e
+
97168e
+static uint64_t get_size_mem_state(DumpState *s)
97168e
+{
97168e
+    return get_mem_state_size_from_len(s->total_size);
97168e
+}
97168e
+
97168e
+static uint64_t get_size_completion_data(DumpState *s)
97168e
+{
97168e
+    return kvm_s390_pv_dmp_get_size_completion_data();
97168e
+}
97168e
+
97168e
+/* PV dump section data functions*/
97168e
+static int get_data_completion(DumpState *s, uint8_t *buff)
97168e
+{
97168e
+    int rc;
97168e
+
97168e
+    if (!pv_dump_initialized) {
97168e
+        return 0;
97168e
+    }
97168e
+    rc = kvm_s390_dump_completion_data(buff);
97168e
+    if (!rc) {
97168e
+            pv_dump_initialized = false;
97168e
+    }
97168e
+    return rc;
97168e
+}
97168e
+
97168e
+static int get_mem_state(DumpState *s, uint8_t *buff)
97168e
+{
97168e
+    int64_t memblock_size, memblock_start;
97168e
+    GuestPhysBlock *block;
97168e
+    uint64_t off;
97168e
+    int rc;
97168e
+
97168e
+    QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) {
97168e
+        memblock_start = dump_filtered_memblock_start(block, s->filter_area_begin,
97168e
+                                                      s->filter_area_length);
97168e
+        if (memblock_start == -1) {
97168e
+            continue;
97168e
+        }
97168e
+
97168e
+        memblock_size = dump_filtered_memblock_size(block, s->filter_area_begin,
97168e
+                                                    s->filter_area_length);
97168e
+
97168e
+        off = get_mem_state_size_from_len(block->target_start);
97168e
+
97168e
+        rc = kvm_s390_dump_mem_state(block->target_start,
97168e
+                                     get_mem_state_size_from_len(memblock_size),
97168e
+                                     buff + off);
97168e
+        if (rc) {
97168e
+            return rc;
97168e
+        }
97168e
+    }
97168e
+
97168e
+    return 0;
97168e
+}
97168e
+
97168e
+static struct sections {
97168e
+    uint64_t (*sections_size_func)(DumpState *s);
97168e
+    int (*sections_contents_func)(DumpState *s, uint8_t *buff);
97168e
+    char sctn_str[12];
97168e
+} sections[] = {
97168e
+    { get_size_mem_state, get_mem_state, "pv_mem_meta"},
97168e
+    { get_size_completion_data, get_data_completion, "pv_compl"},
97168e
+    {NULL , NULL, ""}
97168e
+};
97168e
+
97168e
+static uint64_t arch_sections_write_hdr(DumpState *s, uint8_t *buff)
97168e
+{
97168e
+    Elf64_Shdr *shdr = (void *)buff;
97168e
+    struct sections *sctn = sections;
97168e
+    uint64_t off = s->section_offset;
97168e
+
97168e
+    if (!pv_dump_initialized) {
97168e
+        return 0;
97168e
+    }
97168e
+
97168e
+    for (; sctn->sections_size_func; off += shdr->sh_size, sctn++, shdr++) {
97168e
+        memset(shdr, 0, sizeof(*shdr));
97168e
+        shdr->sh_type = SHT_PROGBITS;
97168e
+        shdr->sh_offset = off;
97168e
+        shdr->sh_size = sctn->sections_size_func(s);
97168e
+        shdr->sh_name = s->string_table_buf->len;
97168e
+        g_array_append_vals(s->string_table_buf, sctn->sctn_str, sizeof(sctn->sctn_str));
97168e
+    }
97168e
+
97168e
+    return (uintptr_t)shdr - (uintptr_t)buff;
97168e
+}
97168e
+
97168e
+
97168e
+/* Add arch specific number of sections and their respective sizes */
97168e
+static void arch_sections_add(DumpState *s)
97168e
+{
97168e
+    struct sections *sctn = sections;
97168e
+
97168e
+    /*
97168e
+     * We only do a PV dump if we are running a PV guest, KVM supports
97168e
+     * the dump API and we got valid dump length information.
97168e
+     */
97168e
+    if (!s390_is_pv() || !kvm_s390_get_protected_dump() ||
97168e
+        !kvm_s390_pv_info_basic_valid()) {
97168e
+        return;
97168e
+    }
97168e
+
97168e
+    /*
97168e
+     * Start the UV dump process by doing the initialize dump call via
97168e
+     * KVM as the proxy.
97168e
+     */
97168e
+    if (!kvm_s390_dump_init()) {
97168e
+        pv_dump_initialized = true;
97168e
+    } else {
97168e
+        /*
97168e
+         * Dump init failed, maybe the guest owner disabled dumping.
97168e
+         * We'll continue the non-PV dump process since this is no
97168e
+         * reason to crash qemu.
97168e
+         */
97168e
+        return;
97168e
+    }
97168e
+
97168e
+    for (; sctn->sections_size_func; sctn++) {
97168e
+        s->shdr_num += 1;
97168e
+        s->elf_section_data_size += sctn->sections_size_func(s);
97168e
+    }
97168e
+}
97168e
+
97168e
+/*
97168e
+ * After the PV dump has been initialized, the CPU data has been
97168e
+ * fetched and memory has been dumped, we need to grab the tweak data
97168e
+ * and the completion data.
97168e
+ */
97168e
+static int arch_sections_write(DumpState *s, uint8_t *buff)
97168e
+{
97168e
+    struct sections *sctn = sections;
97168e
+    int rc;
97168e
+
97168e
+    if (!pv_dump_initialized) {
97168e
+        return -EINVAL;
97168e
+    }
97168e
+
97168e
+    for (; sctn->sections_size_func; sctn++) {
97168e
+        rc = sctn->sections_contents_func(s, buff);
97168e
+        buff += sctn->sections_size_func(s);
97168e
+        if (rc) {
97168e
+            return rc;
97168e
+        }
97168e
+    }
97168e
+    return 0;
97168e
+}
97168e
+
97168e
 int cpu_get_dump_info(ArchDumpInfo *info,
97168e
                       const struct GuestPhysBlockList *guest_phys_blocks)
97168e
 {
97168e
     info->d_machine = EM_S390;
97168e
     info->d_endian = ELFDATA2MSB;
97168e
     info->d_class = ELFCLASS64;
97168e
-
97168e
+    /*
97168e
+     * This is evaluated for each dump so we can freely switch
97168e
+     * between PV and non-PV.
97168e
+     */
97168e
+    if (s390_is_pv() && kvm_s390_get_protected_dump() &&
97168e
+        kvm_s390_pv_info_basic_valid()) {
97168e
+        info->arch_sections_add_fn = *arch_sections_add;
97168e
+        info->arch_sections_write_hdr_fn = *arch_sections_write_hdr;
97168e
+        info->arch_sections_write_fn = *arch_sections_write;
97168e
+    } else {
97168e
+        info->arch_sections_add_fn = NULL;
97168e
+        info->arch_sections_write_hdr_fn = NULL;
97168e
+        info->arch_sections_write_fn = NULL;
97168e
+    }
97168e
     return 0;
97168e
 }
97168e
 
97168e
@@ -261,7 +466,7 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus)
97168e
 {
97168e
     int name_size = 8; /* "LINUX" or "CORE" + pad */
97168e
     size_t elf_note_size = 0;
97168e
-    int note_head_size;
97168e
+    int note_head_size, content_size;
97168e
     const NoteFuncDesc *nf;
97168e
 
97168e
     assert(class == ELFCLASS64);
97168e
@@ -270,12 +475,15 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus)
97168e
     note_head_size = sizeof(Elf64_Nhdr);
97168e
 
97168e
     for (nf = note_core; nf->note_contents_func; nf++) {
97168e
-        elf_note_size = elf_note_size + note_head_size + name_size +
97168e
-                        nf->contents_size;
97168e
+        elf_note_size = elf_note_size + note_head_size + name_size + nf->contents_size;
97168e
     }
97168e
     for (nf = note_linux; nf->note_contents_func; nf++) {
97168e
+        if (nf->pvonly && !s390_is_pv()) {
97168e
+            continue;
97168e
+        }
97168e
+        content_size = nf->contents_size ? nf->contents_size : nf->note_size_func();
97168e
         elf_note_size = elf_note_size + note_head_size + name_size +
97168e
-                        nf->contents_size;
97168e
+                        content_size;
97168e
     }
97168e
 
97168e
     return (elf_note_size) * nr_cpus;
97168e
-- 
97168e
2.37.3
97168e