#include "sysemu/sysemu.h"
#include "sysemu/memory_mapping.h"
#include "sysemu/cpus.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-misc.h"
+#include "qapi/qapi-events-misc.h"
#include "qapi/qmp/qerror.h"
-#include "qmp-commands.h"
-#include "qapi-event.h"
+#include "qemu/error-report.h"
+#include "hw/misc/vmcoreinfo.h"
+
+#ifdef TARGET_X86_64
+#include "win_dump.h"
+#endif
#include <zlib.h>
#ifdef CONFIG_LZO
#define ELF_MACHINE_UNAME "Unknown"
#endif
+#define MAX_GUEST_NOTE_SIZE (1 << 20) /* 1MB should be enough */
+
+#define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \
+ ((DIV_ROUND_UP((hdr_size), 4) + \
+ DIV_ROUND_UP((name_size), 4) + \
+ DIV_ROUND_UP((desc_size), 4)) * 4)
+
uint16_t cpu_to_dump16(DumpState *s, uint16_t val)
{
if (s->dump_info.d_endian == ELFDATA2LSB) {
guest_phys_blocks_free(&s->guest_phys_blocks);
memory_mapping_list_free(&s->list);
close(s->fd);
+ g_free(s->guest_note);
+ s->guest_note = NULL;
if (s->resume) {
if (s->detached) {
qemu_mutex_lock_iothread();
written_size = qemu_write_full(s->fd, buf, size);
if (written_size != size) {
- return -1;
+ return -errno;
}
return 0;
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
if (ret < 0) {
- error_setg(errp, "dump: failed to write elf header");
+ error_setg_errno(errp, -ret, "dump: failed to write elf header");
}
}
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
if (ret < 0) {
- error_setg(errp, "dump: failed to write elf header");
+ error_setg_errno(errp, -ret, "dump: failed to write elf header");
}
}
phdr.p_paddr = cpu_to_dump64(s, memory_mapping->phys_addr);
phdr.p_filesz = cpu_to_dump64(s, filesz);
phdr.p_memsz = cpu_to_dump64(s, memory_mapping->length);
- phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr);
+ phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr) ?: phdr.p_paddr;
assert(memory_mapping->length >= filesz);
ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
if (ret < 0) {
- error_setg(errp, "dump: failed to write program header table");
+ error_setg_errno(errp, -ret,
+ "dump: failed to write program header table");
}
}
phdr.p_paddr = cpu_to_dump32(s, memory_mapping->phys_addr);
phdr.p_filesz = cpu_to_dump32(s, filesz);
phdr.p_memsz = cpu_to_dump32(s, memory_mapping->length);
- phdr.p_vaddr = cpu_to_dump32(s, memory_mapping->virt_addr);
+ phdr.p_vaddr =
+ cpu_to_dump32(s, memory_mapping->virt_addr) ?: phdr.p_paddr;
assert(memory_mapping->length >= filesz);
ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
if (ret < 0) {
- error_setg(errp, "dump: failed to write program header table");
+ error_setg_errno(errp, -ret,
+ "dump: failed to write program header table");
}
}
ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
if (ret < 0) {
- error_setg(errp, "dump: failed to write program header table");
+ error_setg_errno(errp, -ret,
+ "dump: failed to write program header table");
}
}
return cpu->cpu_index + 1;
}
+static void write_guest_note(WriteCoreDumpFunction f, DumpState *s,
+ Error **errp)
+{
+ int ret;
+
+ if (s->guest_note) {
+ ret = f(s->guest_note, s->guest_note_size, s);
+ if (ret < 0) {
+ error_setg(errp, "dump: failed to write guest note");
+ }
+ }
+}
+
static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s,
Error **errp)
{
return;
}
}
+
+ write_guest_note(f, s, errp);
}
static void write_elf32_note(DumpState *s, Error **errp)
ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
if (ret < 0) {
- error_setg(errp, "dump: failed to write program header table");
+ error_setg_errno(errp, -ret,
+ "dump: failed to write program header table");
}
}
return;
}
}
+
+ write_guest_note(f, s, errp);
}
static void write_elf_section(DumpState *s, int type, Error **errp)
ret = fd_write_vmcore(&shdr, shdr_size, s);
if (ret < 0) {
- error_setg(errp, "dump: failed to write section header table");
+ error_setg_errno(errp, -ret,
+ "dump: failed to write section header table");
}
}
ret = fd_write_vmcore(buf, length, s);
if (ret < 0) {
- error_setg(errp, "dump: failed to save memory");
+ error_setg_errno(errp, -ret, "dump: failed to save memory");
} else {
s->written_size += length;
}
return 0;
}
+/*
+ * This function retrieves various sizes from an elf header.
+ *
+ * @note has to be a valid ELF note. The return sizes are unmodified
+ * (not padded or rounded up to be multiple of 4).
+ */
+static void get_note_sizes(DumpState *s, const void *note,
+ uint64_t *note_head_size,
+ uint64_t *name_size,
+ uint64_t *desc_size)
+{
+ uint64_t note_head_sz;
+ uint64_t name_sz;
+ uint64_t desc_sz;
+
+ if (s->dump_info.d_class == ELFCLASS64) {
+ const Elf64_Nhdr *hdr = note;
+ note_head_sz = sizeof(Elf64_Nhdr);
+ name_sz = tswap64(hdr->n_namesz);
+ desc_sz = tswap64(hdr->n_descsz);
+ } else {
+ const Elf32_Nhdr *hdr = note;
+ note_head_sz = sizeof(Elf32_Nhdr);
+ name_sz = tswap32(hdr->n_namesz);
+ desc_sz = tswap32(hdr->n_descsz);
+ }
+
+ if (note_head_size) {
+ *note_head_size = note_head_sz;
+ }
+ if (name_size) {
+ *name_size = name_sz;
+ }
+ if (desc_size) {
+ *desc_size = desc_sz;
+ }
+}
+
+static bool note_name_equal(DumpState *s,
+ const uint8_t *note, const char *name)
+{
+ int len = strlen(name) + 1;
+ uint64_t head_size, name_size;
+
+ get_note_sizes(s, note, &head_size, &name_size, NULL);
+ head_size = ROUND_UP(head_size, 4);
+
+ return name_size == len && memcmp(note + head_size, name, len) == 0;
+}
+
/* write common header, sub header and elf note to vmcore */
static void create_header32(DumpState *s, Error **errp)
{
size = sizeof(DiskDumpHeader32);
dh = g_malloc0(size);
- strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE));
+ memcpy(dh->signature, KDUMP_SIGNATURE, SIG_LEN);
dh->header_version = cpu_to_dump32(s, 6);
block_size = s->dump_info.page_size;
dh->block_size = cpu_to_dump32(s, block_size);
kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL);
offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size;
+ if (s->guest_note &&
+ note_name_equal(s, s->guest_note, "VMCOREINFO")) {
+ uint64_t hsize, name_size, size_vmcoreinfo_desc, offset_vmcoreinfo;
+
+ get_note_sizes(s, s->guest_note,
+ &hsize, &name_size, &size_vmcoreinfo_desc);
+ offset_vmcoreinfo = offset_note + s->note_size - s->guest_note_size +
+ (DIV_ROUND_UP(hsize, 4) + DIV_ROUND_UP(name_size, 4)) * 4;
+ kh->offset_vmcoreinfo = cpu_to_dump64(s, offset_vmcoreinfo);
+ kh->size_vmcoreinfo = cpu_to_dump32(s, size_vmcoreinfo_desc);
+ }
+
kh->offset_note = cpu_to_dump64(s, offset_note);
kh->note_size = cpu_to_dump32(s, s->note_size);
size = sizeof(DiskDumpHeader64);
dh = g_malloc0(size);
- strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE));
+ memcpy(dh->signature, KDUMP_SIGNATURE, SIG_LEN);
dh->header_version = cpu_to_dump32(s, 6);
block_size = s->dump_info.page_size;
dh->block_size = cpu_to_dump32(s, block_size);
kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL);
offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size;
+ if (s->guest_note &&
+ note_name_equal(s, s->guest_note, "VMCOREINFO")) {
+ uint64_t hsize, name_size, size_vmcoreinfo_desc, offset_vmcoreinfo;
+
+ get_note_sizes(s, s->guest_note,
+ &hsize, &name_size, &size_vmcoreinfo_desc);
+ offset_vmcoreinfo = offset_note + s->note_size - s->guest_note_size +
+ (DIV_ROUND_UP(hsize, 4) + DIV_ROUND_UP(name_size, 4)) * 4;
+ kh->offset_vmcoreinfo = cpu_to_dump64(s, offset_vmcoreinfo);
+ kh->size_vmcoreinfo = cpu_to_dump64(s, size_vmcoreinfo_desc);
+ }
+
kh->offset_note = cpu_to_dump64(s, offset_note);
kh->note_size = cpu_to_dump64(s, s->note_size);
{
GuestPhysBlock *last_block;
- last_block = QTAILQ_LAST(&s->guest_phys_blocks.head, GuestPhysBlockHead);
+ last_block = QTAILQ_LAST(&s->guest_phys_blocks.head);
s->max_mapnr = dump_paddr_to_pfn(s, last_block->target_end);
}
return total;
}
+static void vmcoreinfo_update_phys_base(DumpState *s)
+{
+ uint64_t size, note_head_size, name_size, phys_base;
+ char **lines;
+ uint8_t *vmci;
+ size_t i;
+
+ if (!note_name_equal(s, s->guest_note, "VMCOREINFO")) {
+ return;
+ }
+
+ get_note_sizes(s, s->guest_note, ¬e_head_size, &name_size, &size);
+ note_head_size = ROUND_UP(note_head_size, 4);
+
+ vmci = s->guest_note + note_head_size + ROUND_UP(name_size, 4);
+ *(vmci + size) = '\0';
+
+ lines = g_strsplit((char *)vmci, "\n", -1);
+ for (i = 0; lines[i]; i++) {
+ const char *prefix = NULL;
+
+ if (s->dump_info.d_machine == EM_X86_64) {
+ prefix = "NUMBER(phys_base)=";
+ } else if (s->dump_info.d_machine == EM_AARCH64) {
+ prefix = "NUMBER(PHYS_OFFSET)=";
+ }
+
+ if (prefix && g_str_has_prefix(lines[i], prefix)) {
+ if (qemu_strtou64(lines[i] + strlen(prefix), NULL, 16,
+ &phys_base) < 0) {
+ warn_report("Failed to read %s", prefix);
+ } else {
+ s->dump_info.phys_base = phys_base;
+ }
+ break;
+ }
+ }
+
+ g_strfreev(lines);
+}
+
static void dump_init(DumpState *s, int fd, bool has_format,
DumpGuestMemoryFormat format, bool paging, bool has_filter,
int64_t begin, int64_t length, Error **errp)
{
+ VMCoreInfoState *vmci = vmcoreinfo_find();
CPUState *cpu;
int nr_cpus;
Error *err = NULL;
goto cleanup;
}
+ /*
+ * The goal of this block is to (a) update the previously guessed
+ * phys_base, (b) copy the guest note out of the guest.
+ * Failure to do so is not fatal for dumping.
+ */
+ if (vmci) {
+ uint64_t addr, note_head_size, name_size, desc_size;
+ uint32_t size;
+ uint16_t format;
+
+ note_head_size = s->dump_info.d_class == ELFCLASS32 ?
+ sizeof(Elf32_Nhdr) : sizeof(Elf64_Nhdr);
+
+ format = le16_to_cpu(vmci->vmcoreinfo.guest_format);
+ size = le32_to_cpu(vmci->vmcoreinfo.size);
+ addr = le64_to_cpu(vmci->vmcoreinfo.paddr);
+ if (!vmci->has_vmcoreinfo) {
+ warn_report("guest note is not present");
+ } else if (size < note_head_size || size > MAX_GUEST_NOTE_SIZE) {
+ warn_report("guest note size is invalid: %" PRIu32, size);
+ } else if (format != FW_CFG_VMCOREINFO_FORMAT_ELF) {
+ warn_report("guest note format is unsupported: %" PRIu16, format);
+ } else {
+ s->guest_note = g_malloc(size + 1); /* +1 for adding \0 */
+ cpu_physical_memory_read(addr, s->guest_note, size);
+
+ get_note_sizes(s, s->guest_note, NULL, &name_size, &desc_size);
+ s->guest_note_size = ELF_NOTE_SIZE(note_head_size, name_size,
+ desc_size);
+ if (name_size > MAX_GUEST_NOTE_SIZE ||
+ desc_size > MAX_GUEST_NOTE_SIZE ||
+ s->guest_note_size > size) {
+ warn_report("Invalid guest note header");
+ g_free(s->guest_note);
+ s->guest_note = NULL;
+ } else {
+ vmcoreinfo_update_phys_base(s);
+ s->note_size += s->guest_note_size;
+ }
+ }
+ }
+
/* get memory mapping */
if (paging) {
qemu_get_guest_memory_mapping(&s->list, &s->guest_phys_blocks, &err);
Error *local_err = NULL;
DumpQueryResult *result = NULL;
- if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
+ if (s->has_format && s->format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) {
+#ifdef TARGET_X86_64
+ create_win_dump(s, &local_err);
+#endif
+ } else if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
create_kdump_vmcore(s, &local_err);
} else {
create_vmcore(s, &local_err);
/* should never fail */
assert(result);
qapi_event_send_dump_completed(result, !!local_err, (local_err ? \
- error_get_pretty(local_err) : NULL),
- &error_abort);
+ error_get_pretty(local_err) : NULL));
qapi_free_DumpQueryResult(result);
error_propagate(errp, local_err);
}
#endif
+#ifndef TARGET_X86_64
+ if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) {
+ error_setg(errp, "Windows dump is only available for x86-64");
+ return;
+ }
+#endif
+
#if !defined(WIN32)
if (strstart(file, "fd:", &p)) {
fd = monitor_get_fd(cur_mon, p, errp);
item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY;
#endif
+ /* Windows dump is available only if target is x86_64 */
+#ifdef TARGET_X86_64
+ item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList));
+ item = item->next;
+ item->value = DUMP_GUEST_MEMORY_FORMAT_WIN_DMP;
+#endif
+
return cap;
}