Commit 903ef734 authored by Marc-André Lureau's avatar Marc-André Lureau Committed by Michael S. Tsirkin
Browse files

dump: add guest ELF note



Read the guest ELF PT_NOTE from guest memory when fw_cfg
etc/vmcoreinfo entry provides the location, and write it as an
additional note in the dump.

Signed-off-by: default avatarMarc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent 6e43353f
Loading
Loading
Loading
Loading
+107 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@
#include "qapi/qmp/qerror.h"
#include "qmp-commands.h"
#include "qapi-event.h"
#include "qemu/error-report.h"
#include "hw/misc/vmcoreinfo.h"

#include <zlib.h>
#ifdef CONFIG_LZO
@@ -37,6 +39,13 @@
#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) {
@@ -75,6 +84,8 @@ static int dump_cleanup(DumpState *s)
    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();
@@ -234,6 +245,19 @@ static inline int cpu_index(CPUState *cpu)
    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)
{
@@ -257,6 +281,8 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s,
            return;
        }
    }

    write_guest_note(f, s, errp);
}

static void write_elf32_note(DumpState *s, Error **errp)
@@ -302,6 +328,8 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s,
            return;
        }
    }

    write_guest_note(f, s, errp);
}

static void write_elf_section(DumpState *s, int type, Error **errp)
@@ -713,6 +741,44 @@ static int buf_write_note(const void *buf, size_t size, void *opaque)
    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;
    }
}

/* write common header, sub header and elf note to vmcore */
static void create_header32(DumpState *s, Error **errp)
{
@@ -1491,6 +1557,7 @@ 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;
@@ -1568,6 +1635,46 @@ static void dump_init(DumpState *s, int fd, bool has_format,
        goto cleanup;
    }

    /*
     * The goal of this block is to 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 != 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 {
                s->note_size += s->guest_note_size;
            }
        }
    }

    /* get memory mapping */
    if (paging) {
        qemu_get_guest_memory_mapping(&s->list, &s->guest_phys_blocks, &err);
+2 −0
Original line number Diff line number Diff line
@@ -192,6 +192,8 @@ typedef struct DumpState {
                                  * this could be used to calculate
                                  * how much work we have
                                  * finished. */
    uint8_t *guest_note;         /* ELF note content */
    size_t guest_note_size;
} DumpState;

uint16_t cpu_to_dump16(DumpState *s, uint16_t val);