summaryrefslogtreecommitdiffstats
path: root/target/s390x
diff options
context:
space:
mode:
authorStefan Hajnoczi2022-10-26 16:53:48 +0200
committerStefan Hajnoczi2022-10-26 16:53:49 +0200
commit344744e148e6e865f5a57e745b02a87e5ea534ad (patch)
tree2c9a136a0a13c907cbeb17e1adb05989bb8888e4 /target/s390x
parentMerge tag 'pull-tcg-20221026' of https://gitlab.com/rth7680/qemu into staging (diff)
parentdump/win_dump: limit number of processed PRCBs (diff)
downloadqemu-344744e148e6e865f5a57e745b02a87e5ea534ad.tar.gz
qemu-344744e148e6e865f5a57e745b02a87e5ea534ad.tar.xz
qemu-344744e148e6e865f5a57e745b02a87e5ea534ad.zip
Merge tag 'dump-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging
dump queue Hi The "dump" queue, with: - [PATCH v3/v4 0/9] dump: Cleanup and consolidation - [PATCH v4 0/4] dump: add 32-bit guest Windows support # -----BEGIN PGP SIGNATURE----- # # iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmNY9gMcHG1hcmNhbmRy # ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5ZUtD/kByfamsq/8hnS6N/ok # xs9kXO+HZA1A1Kng19RjYWbTka1LpEAf6y6tPtV27l5rWJZxCgqFp3Q2VKQyzAxl # Bcf4gvEhUDJI87jHrZ8WBJ0JvPL8pKNjPn4JUPOQO+6kX8A/3XTwAyvH/T3uxlTo # I+4HLwY0EkJ6NU6Cokud5Uo36Zj7JghKrBxTDrd3NC0qSy8xOoIsB5Pbp2PVKuX2 # F5Zfll3F+NUDsj9zmMR6agP4PBUJUB680TtvMpMZXb2BXumKDLngthCLRtGrgsDh # ChjYr6xkRS9qlXn0PWIYsUyDucDuRFfqTz/Pa9OcGhQuQfIfQiGOM2IFQUE3UcuN # OphJEFi44za3E7xEZziAGIFmro+k8zX2fjgN3+mApxpBjUAF/uzoW1VzIIdx65Gh # H/IguECFu7AwMxPucRUI7PkwexgIcqpufeTRqep2nCFsAwS6bS+obzrAzIMd9kj1 # ApLhj36lkub0Tn77B8bkf1TYJnpBcYbGZpmPCILtOxpBZGlXm++KD1DKAYt6rbnR # 8rQugZNRzEB92aSRTkLJ6QKsqudnbR9ssGbOdEJP+v1fgVtFzYbgygx5QMezGkRw # vRLWrNbDLog+uYpI2Kb30ItU7+bsDrads9n/gqiGvTP887T3alCtRdIq+Fb28oor # tSBhBMqMOtccMy3k+EoXBXX5gw== # =BUEY # -----END PGP SIGNATURE----- # gpg: Signature made Wed 26 Oct 2022 04:55:31 EDT # gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5 # gpg: issuer "marcandre.lureau@redhat.com" # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full] # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full] # Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5 * tag 'dump-pull-request' of https://gitlab.com/marcandre.lureau/qemu: dump/win_dump: limit number of processed PRCBs s390x: pv: Add dump support s390x: Add KVM PV dump interface include/elf.h: add s390x note types s390x: Introduce PV query interface s390x: Add protected dump cap dump: Add architecture section and section string table support dump: Reintroduce memory_offset and section_offset dump: Reorder struct DumpState dump: Write ELF section headers right after ELF header dump: Use a buffer for ELF section data and headers Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'target/s390x')
-rw-r--r--target/s390x/arch_dump.c262
-rw-r--r--target/s390x/kvm/kvm.c7
-rw-r--r--target/s390x/kvm/kvm_s390x.h1
-rw-r--r--target/s390x/kvm/meson.build2
-rw-r--r--target/s390x/kvm/stubs.c12
5 files changed, 257 insertions, 27 deletions
diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c
index f60a14920d..a2329141e8 100644
--- a/target/s390x/arch_dump.c
+++ b/target/s390x/arch_dump.c
@@ -12,11 +12,13 @@
*/
#include "qemu/osdep.h"
+#include "qemu/units.h"
#include "cpu.h"
#include "s390x-internal.h"
#include "elf.h"
#include "sysemu/dump.h"
-
+#include "hw/s390x/pv.h"
+#include "kvm/kvm_s390x.h"
struct S390xUserRegsStruct {
uint64_t psw[2];
@@ -76,9 +78,16 @@ typedef struct noteStruct {
uint64_t todcmp;
uint32_t todpreg;
uint64_t ctrs[16];
+ uint8_t dynamic[1]; /*
+ * Would be a flexible array member, if
+ * that was legal inside a union. Real
+ * size comes from PV info interface.
+ */
} contents;
} QEMU_PACKED Note;
+static bool pv_dump_initialized;
+
static void s390x_write_elf64_prstatus(Note *note, S390CPU *cpu, int id)
{
int i;
@@ -177,28 +186,39 @@ static void s390x_write_elf64_prefix(Note *note, S390CPU *cpu, int id)
note->contents.prefix = cpu_to_be32((uint32_t)(cpu->env.psa));
}
+static void s390x_write_elf64_pv(Note *note, S390CPU *cpu, int id)
+{
+ note->hdr.n_type = cpu_to_be32(NT_S390_PV_CPU_DATA);
+ if (!pv_dump_initialized) {
+ return;
+ }
+ kvm_s390_dump_cpu(cpu, &note->contents.dynamic);
+}
typedef struct NoteFuncDescStruct {
int contents_size;
+ uint64_t (*note_size_func)(void); /* NULL for non-dynamic sized contents */
void (*note_contents_func)(Note *note, S390CPU *cpu, int id);
+ bool pvonly;
} NoteFuncDesc;
static const NoteFuncDesc note_core[] = {
- {sizeof_field(Note, contents.prstatus), s390x_write_elf64_prstatus},
- {sizeof_field(Note, contents.fpregset), s390x_write_elf64_fpregset},
- { 0, NULL}
+ {sizeof_field(Note, contents.prstatus), NULL, s390x_write_elf64_prstatus, false},
+ {sizeof_field(Note, contents.fpregset), NULL, s390x_write_elf64_fpregset, false},
+ { 0, NULL, NULL, false}
};
static const NoteFuncDesc note_linux[] = {
- {sizeof_field(Note, contents.prefix), s390x_write_elf64_prefix},
- {sizeof_field(Note, contents.ctrs), s390x_write_elf64_ctrs},
- {sizeof_field(Note, contents.timer), s390x_write_elf64_timer},
- {sizeof_field(Note, contents.todcmp), s390x_write_elf64_todcmp},
- {sizeof_field(Note, contents.todpreg), s390x_write_elf64_todpreg},
- {sizeof_field(Note, contents.vregslo), s390x_write_elf64_vregslo},
- {sizeof_field(Note, contents.vregshi), s390x_write_elf64_vregshi},
- {sizeof_field(Note, contents.gscb), s390x_write_elf64_gscb},
- { 0, NULL}
+ {sizeof_field(Note, contents.prefix), NULL, s390x_write_elf64_prefix, false},
+ {sizeof_field(Note, contents.ctrs), NULL, s390x_write_elf64_ctrs, false},
+ {sizeof_field(Note, contents.timer), NULL, s390x_write_elf64_timer, false},
+ {sizeof_field(Note, contents.todcmp), NULL, s390x_write_elf64_todcmp, false},
+ {sizeof_field(Note, contents.todpreg), NULL, s390x_write_elf64_todpreg, false},
+ {sizeof_field(Note, contents.vregslo), NULL, s390x_write_elf64_vregslo, false},
+ {sizeof_field(Note, contents.vregshi), NULL, s390x_write_elf64_vregshi, false},
+ {sizeof_field(Note, contents.gscb), NULL, s390x_write_elf64_gscb, false},
+ {0, kvm_s390_pv_dmp_get_size_cpu, s390x_write_elf64_pv, true},
+ { 0, NULL, NULL, false}
};
static int s390x_write_elf64_notes(const char *note_name,
@@ -207,22 +227,41 @@ static int s390x_write_elf64_notes(const char *note_name,
DumpState *s,
const NoteFuncDesc *funcs)
{
- Note note;
+ Note note, *notep;
const NoteFuncDesc *nf;
- int note_size;
+ int note_size, content_size;
int ret = -1;
assert(strlen(note_name) < sizeof(note.name));
for (nf = funcs; nf->note_contents_func; nf++) {
- memset(&note, 0, sizeof(note));
- note.hdr.n_namesz = cpu_to_be32(strlen(note_name) + 1);
- note.hdr.n_descsz = cpu_to_be32(nf->contents_size);
- g_strlcpy(note.name, note_name, sizeof(note.name));
- (*nf->note_contents_func)(&note, cpu, id);
+ notep = &note;
+ if (nf->pvonly && !s390_is_pv()) {
+ continue;
+ }
+
+ content_size = nf->note_size_func ? nf->note_size_func() : nf->contents_size;
+ note_size = sizeof(note) - sizeof(notep->contents) + content_size;
+
+ /* Notes with dynamic sizes need to allocate a note */
+ if (nf->note_size_func) {
+ notep = g_malloc(note_size);
+ }
+
+ memset(notep, 0, sizeof(note));
- note_size = sizeof(note) - sizeof(note.contents) + nf->contents_size;
- ret = f(&note, note_size, s);
+ /* Setup note header data */
+ notep->hdr.n_descsz = cpu_to_be32(content_size);
+ notep->hdr.n_namesz = cpu_to_be32(strlen(note_name) + 1);
+ g_strlcpy(notep->name, note_name, sizeof(notep->name));
+
+ /* Get contents and write them out */
+ (*nf->note_contents_func)(notep, cpu, id);
+ ret = f(notep, note_size, s);
+
+ if (nf->note_size_func) {
+ g_free(notep);
+ }
if (ret < 0) {
return -1;
@@ -247,13 +286,179 @@ int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
return s390x_write_elf64_notes("LINUX", f, cpu, cpuid, s, note_linux);
}
+/* PV dump section size functions */
+static uint64_t get_mem_state_size_from_len(uint64_t len)
+{
+ return (len / (MiB)) * kvm_s390_pv_dmp_get_size_mem_state();
+}
+
+static uint64_t get_size_mem_state(DumpState *s)
+{
+ return get_mem_state_size_from_len(s->total_size);
+}
+
+static uint64_t get_size_completion_data(DumpState *s)
+{
+ return kvm_s390_pv_dmp_get_size_completion_data();
+}
+
+/* PV dump section data functions*/
+static int get_data_completion(DumpState *s, uint8_t *buff)
+{
+ int rc;
+
+ if (!pv_dump_initialized) {
+ return 0;
+ }
+ rc = kvm_s390_dump_completion_data(buff);
+ if (!rc) {
+ pv_dump_initialized = false;
+ }
+ return rc;
+}
+
+static int get_mem_state(DumpState *s, uint8_t *buff)
+{
+ int64_t memblock_size, memblock_start;
+ GuestPhysBlock *block;
+ uint64_t off;
+ int rc;
+
+ QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) {
+ memblock_start = dump_filtered_memblock_start(block, s->filter_area_begin,
+ s->filter_area_length);
+ if (memblock_start == -1) {
+ continue;
+ }
+
+ memblock_size = dump_filtered_memblock_size(block, s->filter_area_begin,
+ s->filter_area_length);
+
+ off = get_mem_state_size_from_len(block->target_start);
+
+ rc = kvm_s390_dump_mem_state(block->target_start,
+ get_mem_state_size_from_len(memblock_size),
+ buff + off);
+ if (rc) {
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static struct sections {
+ uint64_t (*sections_size_func)(DumpState *s);
+ int (*sections_contents_func)(DumpState *s, uint8_t *buff);
+ char sctn_str[12];
+} sections[] = {
+ { get_size_mem_state, get_mem_state, "pv_mem_meta"},
+ { get_size_completion_data, get_data_completion, "pv_compl"},
+ {NULL , NULL, ""}
+};
+
+static uint64_t arch_sections_write_hdr(DumpState *s, uint8_t *buff)
+{
+ Elf64_Shdr *shdr = (void *)buff;
+ struct sections *sctn = sections;
+ uint64_t off = s->section_offset;
+
+ if (!pv_dump_initialized) {
+ return 0;
+ }
+
+ for (; sctn->sections_size_func; off += shdr->sh_size, sctn++, shdr++) {
+ memset(shdr, 0, sizeof(*shdr));
+ shdr->sh_type = SHT_PROGBITS;
+ shdr->sh_offset = off;
+ shdr->sh_size = sctn->sections_size_func(s);
+ shdr->sh_name = s->string_table_buf->len;
+ g_array_append_vals(s->string_table_buf, sctn->sctn_str, sizeof(sctn->sctn_str));
+ }
+
+ return (uintptr_t)shdr - (uintptr_t)buff;
+}
+
+
+/* Add arch specific number of sections and their respective sizes */
+static void arch_sections_add(DumpState *s)
+{
+ struct sections *sctn = sections;
+
+ /*
+ * We only do a PV dump if we are running a PV guest, KVM supports
+ * the dump API and we got valid dump length information.
+ */
+ if (!s390_is_pv() || !kvm_s390_get_protected_dump() ||
+ !kvm_s390_pv_info_basic_valid()) {
+ return;
+ }
+
+ /*
+ * Start the UV dump process by doing the initialize dump call via
+ * KVM as the proxy.
+ */
+ if (!kvm_s390_dump_init()) {
+ pv_dump_initialized = true;
+ } else {
+ /*
+ * Dump init failed, maybe the guest owner disabled dumping.
+ * We'll continue the non-PV dump process since this is no
+ * reason to crash qemu.
+ */
+ return;
+ }
+
+ for (; sctn->sections_size_func; sctn++) {
+ s->shdr_num += 1;
+ s->elf_section_data_size += sctn->sections_size_func(s);
+ }
+}
+
+/*
+ * After the PV dump has been initialized, the CPU data has been
+ * fetched and memory has been dumped, we need to grab the tweak data
+ * and the completion data.
+ */
+static int arch_sections_write(DumpState *s, uint8_t *buff)
+{
+ struct sections *sctn = sections;
+ int rc;
+
+ if (!pv_dump_initialized) {
+ return -EINVAL;
+ }
+
+ for (; sctn->sections_size_func; sctn++) {
+ rc = sctn->sections_contents_func(s, buff);
+ buff += sctn->sections_size_func(s);
+ if (rc) {
+ return rc;
+ }
+ }
+ return 0;
+}
+
int cpu_get_dump_info(ArchDumpInfo *info,
const struct GuestPhysBlockList *guest_phys_blocks)
{
info->d_machine = EM_S390;
info->d_endian = ELFDATA2MSB;
info->d_class = ELFCLASS64;
-
+ /*
+ * This is evaluated for each dump so we can freely switch
+ * between PV and non-PV.
+ */
+ if (s390_is_pv() && kvm_s390_get_protected_dump() &&
+ kvm_s390_pv_info_basic_valid()) {
+ info->arch_sections_add_fn = *arch_sections_add;
+ info->arch_sections_write_hdr_fn = *arch_sections_write_hdr;
+ info->arch_sections_write_fn = *arch_sections_write;
+ } else {
+ info->arch_sections_add_fn = NULL;
+ info->arch_sections_write_hdr_fn = NULL;
+ info->arch_sections_write_fn = NULL;
+ }
return 0;
}
@@ -261,7 +466,7 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus)
{
int name_size = 8; /* "LINUX" or "CORE" + pad */
size_t elf_note_size = 0;
- int note_head_size;
+ int note_head_size, content_size;
const NoteFuncDesc *nf;
assert(class == ELFCLASS64);
@@ -270,12 +475,15 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus)
note_head_size = sizeof(Elf64_Nhdr);
for (nf = note_core; nf->note_contents_func; nf++) {
- elf_note_size = elf_note_size + note_head_size + name_size +
- nf->contents_size;
+ elf_note_size = elf_note_size + note_head_size + name_size + nf->contents_size;
}
for (nf = note_linux; nf->note_contents_func; nf++) {
+ if (nf->pvonly && !s390_is_pv()) {
+ continue;
+ }
+ content_size = nf->contents_size ? nf->contents_size : nf->note_size_func();
elf_note_size = elf_note_size + note_head_size + name_size +
- nf->contents_size;
+ content_size;
}
return (elf_note_size) * nr_cpus;
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index da8c47f57b..3ac7ec9acf 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -158,6 +158,7 @@ static int cap_hpage_1m;
static int cap_vcpu_resets;
static int cap_protected;
static int cap_zpci_op;
+static int cap_protected_dump;
static bool mem_op_storage_key_support;
@@ -364,6 +365,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
cap_vcpu_resets = kvm_check_extension(s, KVM_CAP_S390_VCPU_RESETS);
cap_protected = kvm_check_extension(s, KVM_CAP_S390_PROTECTED);
cap_zpci_op = kvm_check_extension(s, KVM_CAP_S390_ZPCI_OP);
+ cap_protected_dump = kvm_check_extension(s, KVM_CAP_S390_PROTECTED_DUMP);
kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP, 0);
kvm_vm_enable_cap(s, KVM_CAP_S390_VECTOR_REGISTERS, 0);
@@ -2045,6 +2047,11 @@ int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch,
return kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick);
}
+int kvm_s390_get_protected_dump(void)
+{
+ return cap_protected_dump;
+}
+
int kvm_s390_get_ri(void)
{
return cap_ri;
diff --git a/target/s390x/kvm/kvm_s390x.h b/target/s390x/kvm/kvm_s390x.h
index aaae8570de..f9785564d0 100644
--- a/target/s390x/kvm/kvm_s390x.h
+++ b/target/s390x/kvm/kvm_s390x.h
@@ -26,6 +26,7 @@ int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state);
void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu);
int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu);
int kvm_s390_get_hpage_1m(void);
+int kvm_s390_get_protected_dump(void);
int kvm_s390_get_ri(void);
int kvm_s390_get_zpci_op(void);
int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_clock);
diff --git a/target/s390x/kvm/meson.build b/target/s390x/kvm/meson.build
index d1356356b1..aef52b6686 100644
--- a/target/s390x/kvm/meson.build
+++ b/target/s390x/kvm/meson.build
@@ -1,6 +1,8 @@
s390x_ss.add(when: 'CONFIG_KVM', if_true: files(
'kvm.c'
+), if_false: files(
+ 'stubs.c'
))
# Newer kernels on s390 check for an S390_PGSTE program header and
diff --git a/target/s390x/kvm/stubs.c b/target/s390x/kvm/stubs.c
new file mode 100644
index 0000000000..5fd63b9a7e
--- /dev/null
+++ b/target/s390x/kvm/stubs.c
@@ -0,0 +1,12 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "kvm_s390x.h"
+
+int kvm_s390_get_protected_dump(void)
+{
+ return false;
+}