All of lore.kernel.org
 help / color / mirror / Atom feed
From: Wen Congyang <wency@cn.fujitsu.com>
To: qemu-devel <qemu-devel@nongnu.org>,
	Jan Kiszka <jan.kiszka@siemens.com>,
	Dave Anderson <anderson@redhat.com>
Subject: [Qemu-devel] [RFC][PATCH] introduce a new monitor command 'dump' to dump guest's memory
Date: Tue, 29 Nov 2011 13:41:54 +0800	[thread overview]
Message-ID: <4ED470A2.8090407@cn.fujitsu.com> (raw)
In-Reply-To: <4EC373F8.6080300@cn.fujitsu.com>

'virsh dump' can not work when host pci device is used by guest. We have
discussed this issue here:
http://lists.nongnu.org/archive/html/qemu-devel/2011-10/msg00736.html

We have determined to introduce a new command dump to dump memory. The core
file's format is elf.

Note:
1. The guest should be x86 or x86_64. The other arch is not supported.
2. gdb can not convert the virtual address to physical address sometimes, because
   the PT_LOAD in the core file does not contain enough information.
3. If you use old gdb, gdb may crash. I use gdb-7.3.1, and it does not crash.

Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
---
 Makefile.target |    8 +-
 dump.c          |  772 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 dump.h          |    6 +
 hmp-commands.hx |   16 ++
 monitor.c       |    3 +
 qmp-commands.hx |   24 ++
 6 files changed, 825 insertions(+), 4 deletions(-)
 create mode 100644 dump.c
 create mode 100644 dump.h

diff --git a/Makefile.target b/Makefile.target
index a111521..a4f0e6d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -118,7 +118,7 @@ $(call set-vpath, $(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR
 QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user
 obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \
       elfload.o linuxload.o uaccess.o gdbstub.o cpu-uname.o \
-      user-exec.o $(oslib-obj-y)
+      user-exec.o $(oslib-obj-y) dump.o
 
 obj-$(TARGET_HAS_BFLT) += flatload.o
 
@@ -156,7 +156,7 @@ LDFLAGS+=-Wl,-segaddr,__STD_PROG_ZONE,0x1000 -image_base 0x0e000000
 LIBS+=-lmx
 
 obj-y = main.o commpage.o machload.o mmap.o signal.o syscall.o thunk.o \
-        gdbstub.o user-exec.o
+        gdbstub.o user-exec.o dump.o
 
 obj-i386-y += ioport-user.o
 
@@ -178,7 +178,7 @@ $(call set-vpath, $(SRC_PATH)/bsd-user)
 QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH)
 
 obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \
-        gdbstub.o uaccess.o user-exec.o
+        gdbstub.o uaccess.o user-exec.o dump.o
 
 obj-i386-y += ioport-user.o
 
@@ -194,7 +194,7 @@ endif #CONFIG_BSD_USER
 # System emulator target
 ifdef CONFIG_SOFTMMU
 
-obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o ioport.o
+obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o ioport.o dump.o
 # virtio has to be here due to weird dependency between PCI and virtio-net.
 # need to fix this properly
 obj-$(CONFIG_NO_PCI) += pci-stub.o
diff --git a/dump.c b/dump.c
new file mode 100644
index 0000000..0117ac3
--- /dev/null
+++ b/dump.c
@@ -0,0 +1,772 @@
+/*
+ * QEMU live dump
+ *
+ * Copyright Fujitsu, Corp. 2011
+ *
+ * Authors:
+ *     Wen Congyang <wency@cn.fujitsu.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include <unistd.h>
+#include <elf.h>
+#include <sys/procfs.h>
+#include "cpu.h"
+#include "cpu-all.h"
+#include "targphys.h"
+#include "monitor.h"
+#include "kvm.h"
+#include "dump.h"
+#include "sysemu.h"
+#include "bswap.h"
+
+static inline int cpuid(CPUState *env)
+{
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USE_NPTL)
+    return env->host_tid;
+#else
+    return env->cpu_index + 1;
+#endif
+}
+
+#if defined(TARGET_I386)
+
+#ifdef TARGET_X86_64
+typedef struct {
+    target_ulong r15, r14, r13, r12, rbp, rbx, r11, r10;
+    target_ulong r9, r8, rax, rcx, rdx, rsi, rdi, orig_rax;
+    target_ulong rip, cs, eflags;
+    target_ulong rsp, ss;
+    target_ulong fs_base, gs_base;
+    target_ulong ds, es, fs, gs;
+} x86_64_user_regs_struct;
+
+static int x86_64_write_elf64_note(Monitor *mon, int fd, CPUState *env,
+                                   target_phys_addr_t *offset)
+{
+    x86_64_user_regs_struct regs;
+    Elf64_Nhdr *note;
+    char *buf;
+    int descsz, note_size, name_size = 5;
+    const char *name = "CORE";
+    int id = cpuid(env);
+    int ret;
+
+    regs.r15 = env->regs[15];
+    regs.r14 = env->regs[14];
+    regs.r13 = env->regs[13];
+    regs.r12 = env->regs[12];
+    regs.r11 = env->regs[11];
+    regs.r10 = env->regs[10];
+    regs.r9  = env->regs[9];
+    regs.r8  = env->regs[8];
+    regs.rbp = env->regs[R_EBP];
+    regs.rsp = env->regs[R_ESP];
+    regs.rdi = env->regs[R_EDI];
+    regs.rsi = env->regs[R_ESI];
+    regs.rdx = env->regs[R_EDX];
+    regs.rcx = env->regs[R_ECX];
+    regs.rbx = env->regs[R_EBX];
+    regs.rax = env->regs[R_EAX];
+    regs.rip = env->eip;
+    regs.eflags = env->eflags;
+
+    regs.orig_rax = 0; /* FIXME */
+    regs.cs = env->segs[R_CS].selector;
+    regs.ss = env->segs[R_SS].selector;
+    regs.fs_base = env->segs[R_FS].base;
+    regs.gs_base = env->segs[R_GS].base;
+    regs.ds = env->segs[R_DS].selector;
+    regs.es = env->segs[R_ES].selector;
+    regs.fs = env->segs[R_FS].selector;
+    regs.gs = env->segs[R_GS].selector;
+
+    descsz = 336; /* sizeof(prstatus_t) is 336 on x86_64 box */
+    note_size = ((sizeof(Elf64_Nhdr) + 3) / 4 + (name_size + 3) / 4 +
+                (descsz + 3) / 4) * 4;
+    note = g_malloc(note_size);
+
+    memset(note, 0, note_size);
+    note->n_namesz = cpu_to_le32(name_size);
+    note->n_descsz = cpu_to_le32(descsz);
+    note->n_type = cpu_to_le32(NT_PRSTATUS);
+    buf = (char *)note;
+    buf += ((sizeof(Elf64_Nhdr) + 3) / 4) * 4;
+    memcpy(buf, name, name_size);
+    buf += ((name_size + 3) / 4) * 4;
+    memcpy(buf + 32, &id, 4); /* pr_pid */
+    buf += descsz - sizeof(x86_64_user_regs_struct)-sizeof(target_ulong);
+    memcpy(buf, &regs, sizeof(x86_64_user_regs_struct));
+
+    lseek(fd, *offset, SEEK_SET);
+    ret = write(fd, note, note_size);
+    g_free(note);
+    if (ret < 0) {
+        monitor_printf(mon, "dump: failed to write elf prstatus.\n");
+        return -1;
+    }
+
+    *offset += note_size;
+
+    return 0;
+}
+#endif
+
+/* This function is copied from crash, gdb also needs this virtual address */
+static target_ulong get_phys_base_addr(CPUState *env, target_ulong *base_vaddr)
+{
+    int i;
+    target_ulong kernel_base = -1;
+    target_ulong last, mask;
+
+    for (i = 30, last = -1; (kernel_base == -1) && (i >= 20); i--) {
+        mask = ~((1LL << i) - 1);
+        *base_vaddr = env->idt.base & mask;
+        if (*base_vaddr == last) {
+            continue;
+        }
+
+        kernel_base = cpu_get_phys_page_debug(env, *base_vaddr);
+        last = *base_vaddr;
+    }
+
+    return kernel_base;
+}
+
+/* This function likes get_phys_base_addr(). */
+static target_ulong get_page_offset_addr(CPUState *env,
+                                         target_ulong *page_offset_vaddr)
+{
+    int i;
+    target_ulong kernel_base = -1;
+    target_ulong last, mask;
+
+    for (i = 30, last = -1; (kernel_base == -1) && (i >= 20); i--) {
+        mask = ~((1LL << i) - 1);
+        *page_offset_vaddr = env->gdt.base & mask;
+        if (*page_offset_vaddr == last) {
+            continue;
+        }
+
+        kernel_base = cpu_get_phys_page_debug(env, *page_offset_vaddr);
+        last = *page_offset_vaddr;
+    }
+
+    return kernel_base;
+}
+
+typedef struct {
+    uint32_t ebx, ecx, edx, esi, edi, ebp, eax;
+    unsigned short ds, __ds, es, __es;
+    unsigned short fs, __fs, gs, __gs;
+    uint32_t orig_eax, eip;
+    unsigned short cs, __cs;
+    uint32_t eflags, esp;
+    unsigned short ss, __ss;
+} x86_user_regs_struct;
+
+static int x86_write_elf64_note(Monitor *mon, int fd, CPUState *env,
+                                target_phys_addr_t *offset)
+{
+    x86_user_regs_struct regs;
+    Elf64_Nhdr *note;
+    char *buf;
+    int descsz, note_size, name_size = 5;
+    const char *name = "CORE";
+    int id = cpuid(env);
+    int ret;
+
+    regs.ebp = env->regs[R_EBP] & 0xffffffff;
+    regs.esp = env->regs[R_ESP] & 0xffffffff;
+    regs.edi = env->regs[R_EDI] & 0xffffffff;
+    regs.esi = env->regs[R_ESI] & 0xffffffff;
+    regs.edx = env->regs[R_EDX] & 0xffffffff;
+    regs.ecx = env->regs[R_ECX] & 0xffffffff;
+    regs.ebx = env->regs[R_EBX] & 0xffffffff;
+    regs.eax = env->regs[R_EAX] & 0xffffffff;
+    regs.eip = env->eip & 0xffffffff;
+    regs.eflags = env->eflags & 0xffffffff;
+
+    regs.cs = env->segs[R_CS].selector;
+    regs.__cs = 0;
+    regs.ss = env->segs[R_SS].selector;
+    regs.__ss = 0;
+    regs.ds = env->segs[R_DS].selector;
+    regs.__ds = 0;
+    regs.es = env->segs[R_ES].selector;
+    regs.__es = 0;
+    regs.fs = env->segs[R_FS].selector;
+    regs.__fs = 0;
+    regs.gs = env->segs[R_GS].selector;
+    regs.__gs = 0;
+
+    descsz = 144; /* sizeof(prstatus_t) is 144 on x86 box */
+    note_size = ((sizeof(Elf64_Nhdr) + 3) / 4 + (name_size + 3) / 4 +
+                (descsz + 3) / 4) * 4;
+    note = g_malloc(note_size);
+
+    memset(note, 0, note_size);
+    note->n_namesz = cpu_to_le32(name_size);
+    note->n_descsz = cpu_to_le32(descsz);
+    note->n_type = cpu_to_le32(NT_PRSTATUS);
+    buf = (char *)note;
+    buf += ((sizeof(Elf64_Nhdr) + 3) / 4) * 4;
+    memcpy(buf, name, name_size);
+    buf += ((name_size + 3) / 4) * 4;
+    memcpy(buf + 24, &id, 4); /* pr_pid */
+    buf += descsz - sizeof(x86_user_regs_struct)-4;
+    memcpy(buf, &regs, sizeof(x86_user_regs_struct));
+
+    lseek(fd, *offset, SEEK_SET);
+    ret = write(fd, note, note_size);
+    g_free(note);
+    if (ret < 0) {
+        monitor_printf(mon, "dump: failed to write elf prstatus.\n");
+        return -1;
+    }
+
+    *offset += note_size;
+
+    return 0;
+}
+
+static int x86_write_elf32_note(Monitor *mon, int fd, CPUState *env,
+                                target_phys_addr_t *offset)
+{
+    x86_user_regs_struct regs;
+    Elf32_Nhdr *note;
+    char *buf;
+    int descsz, note_size, name_size = 5;
+    const char *name = "CORE";
+    int id = cpuid(env);
+    int ret;
+
+    regs.ebp = env->regs[R_EBP] & 0xffffffff;
+    regs.esp = env->regs[R_ESP] & 0xffffffff;
+    regs.edi = env->regs[R_EDI] & 0xffffffff;
+    regs.esi = env->regs[R_ESI] & 0xffffffff;
+    regs.edx = env->regs[R_EDX] & 0xffffffff;
+    regs.ecx = env->regs[R_ECX] & 0xffffffff;
+    regs.ebx = env->regs[R_EBX] & 0xffffffff;
+    regs.eax = env->regs[R_EAX] & 0xffffffff;
+    regs.eip = env->eip & 0xffffffff;
+    regs.eflags = env->eflags & 0xffffffff;
+
+    regs.cs = env->segs[R_CS].selector;
+    regs.__cs = 0;
+    regs.ss = env->segs[R_SS].selector;
+    regs.__ss = 0;
+    regs.ds = env->segs[R_DS].selector;
+    regs.__ds = 0;
+    regs.es = env->segs[R_ES].selector;
+    regs.__es = 0;
+    regs.fs = env->segs[R_FS].selector;
+    regs.__fs = 0;
+    regs.gs = env->segs[R_GS].selector;
+    regs.__gs = 0;
+
+    descsz = 144; /* sizeof(prstatus_t) is 144 on x86 box */
+    note_size = ((sizeof(Elf32_Nhdr) + 3) / 4 + (name_size + 3) / 4 +
+                (descsz + 3) / 4) * 4;
+    note = g_malloc(note_size);
+
+    memset(note, 0, note_size);
+    note->n_namesz = cpu_to_le32(name_size);
+    note->n_descsz = cpu_to_le32(descsz);
+    note->n_type = cpu_to_le32(NT_PRSTATUS);
+    buf = (char *)note;
+    buf += ((sizeof(Elf32_Nhdr) + 3) / 4) * 4;
+    memcpy(buf, name, name_size);
+    buf += ((name_size + 3) / 4) * 4;
+    memcpy(buf + 24, &id, 4); /* pr_pid */
+    buf += descsz - sizeof(x86_user_regs_struct)-4;
+    memcpy(buf, &regs, sizeof(x86_user_regs_struct));
+
+    lseek(fd, *offset, SEEK_SET);
+    ret = write(fd, note, note_size);
+    g_free(note);
+    if (ret < 0) {
+        monitor_printf(mon, "dump: failed to write elf prstatus.\n");
+        return -1;
+    }
+
+    *offset += note_size;
+
+    return 0;
+}
+#endif
+
+static int write_elf64_header(Monitor *mon, int fd, int phdr_num, int machine,
+                              int endian)
+{
+    Elf64_Ehdr elf_header;
+    int ret;
+
+    memset(&elf_header, 0, sizeof(Elf64_Ehdr));
+    memcpy(&elf_header, ELFMAG, 4);
+    elf_header.e_ident[EI_CLASS] = ELFCLASS64;
+    elf_header.e_ident[EI_DATA] = endian;
+    elf_header.e_ident[EI_VERSION] = EV_CURRENT;
+    elf_header.e_type = cpu_to_le16(ET_CORE);
+    elf_header.e_machine = cpu_to_le16(machine);
+    elf_header.e_version = cpu_to_le32(EV_CURRENT);
+    elf_header.e_ehsize = cpu_to_le16(sizeof(elf_header));
+    elf_header.e_phoff = cpu_to_le64(sizeof(Elf64_Ehdr));
+    elf_header.e_phentsize = cpu_to_le16(sizeof(Elf64_Phdr));
+    elf_header.e_phnum = cpu_to_le16(phdr_num);
+
+    lseek(fd, 0, SEEK_SET);
+    ret = write(fd, &elf_header, sizeof(elf_header));
+    if (ret < 0) {
+        monitor_printf(mon, "dump: failed to write elf header.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_elf32_header(Monitor *mon, int fd, int phdr_num, int machine,
+                              int endian)
+{
+    Elf32_Ehdr elf_header;
+    int ret;
+
+    memset(&elf_header, 0, sizeof(Elf32_Ehdr));
+    memcpy(&elf_header, ELFMAG, 4);
+    elf_header.e_ident[EI_CLASS] = ELFCLASS32;
+    elf_header.e_ident[EI_DATA] = endian;
+    elf_header.e_ident[EI_VERSION] = EV_CURRENT;
+    elf_header.e_type = cpu_to_le16(ET_CORE);
+    elf_header.e_machine = cpu_to_le16(machine);
+    elf_header.e_version = cpu_to_le32(EV_CURRENT);
+    elf_header.e_ehsize = cpu_to_le16(sizeof(elf_header));
+    elf_header.e_phoff = cpu_to_le32(sizeof(Elf32_Ehdr));
+    elf_header.e_phentsize = cpu_to_le16(sizeof(Elf32_Phdr));
+    elf_header.e_phnum = cpu_to_le16(phdr_num);
+
+    lseek(fd, 0, SEEK_SET);
+    ret = write(fd, &elf_header, sizeof(elf_header));
+    if (ret < 0) {
+        monitor_printf(mon, "dump: failed to write elf header.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_elf64_load(Monitor *mon, int fd, RAMBlock *block,
+                            int phdr_index, target_phys_addr_t *offset,
+                            target_ulong base_vaddr)
+{
+    Elf64_Phdr phdr;
+    off_t phdr_offset;
+    int ret;
+
+    memset(&phdr, 0, sizeof(Elf64_Phdr));
+    phdr.p_type = cpu_to_le32(PT_LOAD);
+    phdr.p_offset = cpu_to_le64(*offset);
+    phdr.p_paddr = cpu_to_le64(block->offset);
+    phdr.p_filesz = cpu_to_le64(block->length);
+    phdr.p_memsz = cpu_to_le64(block->length);
+    phdr.p_vaddr = cpu_to_le64(base_vaddr);
+
+    phdr_offset = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr)*phdr_index;
+    lseek(fd, phdr_offset, SEEK_SET);
+    ret = write(fd, &phdr, sizeof(Elf64_Phdr));
+    if (ret < 0) {
+        monitor_printf(mon, "dump: failed to write program header table.\n");
+        return -1;
+    }
+
+    lseek(fd, *offset, SEEK_SET);
+    ret = write(fd, block->host, block->length);
+    if (ret < 0) {
+        monitor_printf(mon, "dump: failed to write program segment.\n");
+        return -1;
+    }
+    *offset += block->length;
+
+    return 0;
+}
+
+static int write_elf32_load(Monitor *mon, int fd, RAMBlock *block,
+                            int phdr_index, target_phys_addr_t *offset,
+                            target_ulong base_vaddr)
+{
+    Elf32_Phdr phdr;
+    off_t phdr_offset;
+    int ret;
+
+    memset(&phdr, 0, sizeof(Elf32_Phdr));
+    phdr.p_type = cpu_to_le32(PT_LOAD);
+    phdr.p_offset = cpu_to_le32(*offset);
+    phdr.p_paddr = cpu_to_le32(block->offset);
+    phdr.p_filesz = cpu_to_le32(block->length);
+    phdr.p_memsz = cpu_to_le32(block->length);
+    phdr.p_vaddr = cpu_to_le32(base_vaddr);
+
+    phdr_offset = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr)*phdr_index;
+    lseek(fd, phdr_offset, SEEK_SET);
+    ret = write(fd, &phdr, sizeof(Elf32_Phdr));
+    if (ret < 0) {
+        monitor_printf(mon, "dump: failed to write program header table.\n");
+        return -1;
+    }
+
+    lseek(fd, *offset, SEEK_SET);
+    ret = write(fd, block->host, block->length);
+    if (ret < 0) {
+        monitor_printf(mon, "dump: failed to write program segment.\n");
+        return -1;
+    }
+    *offset += block->length;
+
+    return 0;
+}
+
+static int write_elf_load(Monitor *mon, int fd, RAMBlock *block,
+                          int phdr_index, target_phys_addr_t *offset,
+                          target_ulong virtual_addr,
+                          target_phys_addr_t phys_addr,
+                          target_phys_addr_t length, int type)
+{
+    RAMBlock temp_block;
+    int ret;
+
+    temp_block.host = block->host + (phys_addr - block->offset);
+    temp_block.offset = phys_addr;
+    temp_block.length = length;
+    if (type == 1) {
+        ret = write_elf64_load(mon, fd, &temp_block, phdr_index,
+                               offset, virtual_addr);
+    } else  {
+        ret = write_elf32_load(mon, fd, &temp_block, phdr_index,
+                               offset, virtual_addr);
+    }
+    return ret;
+}
+
+static int write_elf64_notes(Monitor *mon, int fd, int phdr_index,
+                             target_phys_addr_t *offset, bool lma)
+{
+    CPUState *env;
+    int ret;
+    target_phys_addr_t begin = *offset;
+    Elf64_Phdr phdr;
+    off_t phdr_offset;
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+#if defined(TARGET_I386)
+#ifdef TARGET_X86_64
+        if (lma) {
+            ret = x86_64_write_elf64_note(mon, fd, env, offset);
+        } else {
+#endif
+            ret = x86_write_elf64_note(mon, fd, env, offset);
+#ifdef TARGET_X86_64
+        }
+#endif
+#else
+        ret = -1; /* Not supported */
+#endif
+
+        if (ret < 0) {
+            monitor_printf(mon, "dump: failed to write elf notes.\n");
+            return -1;
+        }
+    }
+
+    memset(&phdr, 0, sizeof(Elf64_Phdr));
+    phdr.p_type = cpu_to_le32(PT_NOTE);
+    phdr.p_offset = cpu_to_le64(begin);
+    phdr.p_paddr = 0;
+    phdr.p_filesz = cpu_to_le64(*offset - begin);
+    phdr.p_memsz = cpu_to_le64(*offset - begin);
+    phdr.p_vaddr = 0;
+
+    phdr_offset = sizeof(Elf64_Ehdr);
+    lseek(fd, phdr_offset, SEEK_SET);
+    ret = write(fd, &phdr, sizeof(Elf64_Phdr));
+    if (ret < 0) {
+        monitor_printf(mon, "dump: failed to write program header table.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_elf32_notes(Monitor *mon, int fd, int phdr_index,
+                             target_phys_addr_t *offset)
+{
+    CPUState *env;
+    int ret;
+    target_phys_addr_t begin = *offset;
+    Elf32_Phdr phdr;
+    off_t phdr_offset;
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+#if defined(TARGET_I386)
+        ret = x86_write_elf32_note(mon, fd, env, offset);
+#else
+        ret = -1; /* Not supported */
+#endif
+
+        if (ret < 0) {
+            monitor_printf(mon, "dump: failed to write elf notes.\n");
+            return -1;
+        }
+    }
+
+    memset(&phdr, 0, sizeof(Elf32_Phdr));
+    phdr.p_type = cpu_to_le32(PT_NOTE);
+    phdr.p_offset = cpu_to_le32(begin);
+    phdr.p_paddr = 0;
+    phdr.p_filesz = cpu_to_le32(*offset - begin);
+    phdr.p_memsz = cpu_to_le32(*offset - begin);
+    phdr.p_vaddr = 0;
+
+    phdr_offset = sizeof(Elf32_Ehdr);
+    lseek(fd, phdr_offset, SEEK_SET);
+    ret = write(fd, &phdr, sizeof(Elf32_Phdr));
+    if (ret < 0) {
+        monitor_printf(mon, "dump: failed to write program header table.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int create_vmcore(Monitor *mon, int fd)
+{
+    CPUState *env;
+    target_ulong kernel_base = -1, base_vaddr;
+    target_ulong page_offset = -1, page_offset_vaddr;
+    target_phys_addr_t offset;
+    int phdr_num, phdr_index;
+    RAMBlock *block;
+    bool lma = false;
+    int ret;
+    int type, machine, endian;
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        cpu_synchronize_state(env);
+    }
+
+#if defined(TARGET_I386)
+
+#ifdef TARGET_X86_64
+    lma = !!(first_cpu->hflags & HF_LMA_MASK);
+#endif
+
+    kernel_base = get_phys_base_addr(first_cpu, &base_vaddr);
+    if (kernel_base == -1) {
+        monitor_printf(mon, "dump: can not get phys_base\n");
+        return -1;
+    }
+    page_offset = get_page_offset_addr(first_cpu, &page_offset_vaddr);
+    if (page_offset == -1) {
+        monitor_printf(mon, "dump: can not get page_offset\n");
+        return -1;
+    }
+#endif
+
+#if defined(TARGET_I386)
+    if (lma) {
+        machine = EM_X86_64;
+    } else {
+        machine = EM_386;
+    }
+    endian = ELFDATA2LSB;
+#else
+    monitor_printf(mon, "dump: unsupported target.\n")
+    return -1;
+#endif
+
+    if (sizeof(ram_addr_t) == 4) {
+        type = 0; /* use elf32 */
+#if defined(TARGET_I386)
+    } else if (!lma) {
+        type = 0; /* the guest os is not in IA-32e mode */
+#endif
+    } else {
+        type = 1; /* use elf64 */
+    }
+
+    phdr_num = 1; /* PT_NOTE */
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+#if defined(TARGET_I386)
+        if (kernel_base > block->offset &&
+            kernel_base < (block->offset + block->length)) {
+            phdr_num++;
+        }
+        if (page_offset != kernel_base && page_offset > block->offset &&
+            page_offset < (block->offset + block->length)) {
+            phdr_num++;
+        }
+        if (!lma && (block->offset + block->length > UINT_MAX)) {
+            type = 1; /* The memory size is greater than 4G */
+        }
+#endif
+        phdr_num++;
+    }
+
+    if (type == 1) {
+        ret = write_elf64_header(mon, fd, phdr_num, machine, endian);
+    } else {
+        ret = write_elf32_header(mon, fd, phdr_num, machine, endian);
+    }
+    if (ret < 0) {
+        return -1;
+    }
+
+    phdr_index = 0;
+    if (type == 1) {
+        offset = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr)*phdr_num;
+        ret = write_elf64_notes(mon, fd, phdr_index++, &offset, lma);
+    } else {
+        offset = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr)*phdr_num;
+        ret = write_elf32_notes(mon, fd, phdr_index++, &offset);
+    }
+
+    if (ret < 0) {
+        return -1;
+    }
+
+#if defined(TARGET_I386)
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+        bool kernel_base_in_block, page_offset_in_block;
+        target_ulong length;
+        target_phys_addr_t paddr, vaddr;
+
+        kernel_base_in_block = kernel_base >= block->offset &&
+                               kernel_base < (block->offset + block->length);
+        page_offset_in_block = page_offset >= block->offset &&
+                               page_offset < (block->offset + block->length);
+        if (!kernel_base_in_block && !page_offset_in_block) {
+            continue;
+        }
+
+        if (kernel_base_in_block) {
+            paddr = kernel_base;
+            vaddr = base_vaddr;
+            if (page_offset_in_block && page_offset > paddr) {
+                length = page_offset - paddr;
+            } else {
+                length = block->length - (paddr - block->offset);
+            }
+
+            ret = write_elf_load(mon, fd, block, phdr_index++, &offset,
+                                 vaddr, paddr, length, type);
+            if (ret < 0) {
+                return -1;
+            }
+        }
+
+        if (page_offset != kernel_base && page_offset_in_block) {
+            paddr = page_offset;
+            vaddr = page_offset_vaddr;
+            if (kernel_base_in_block && kernel_base > paddr) {
+                length = kernel_base - paddr;
+            } else {
+                length = block->length - (paddr - block->offset);
+            }
+
+            ret = write_elf_load(mon, fd, block, phdr_index++, &offset,
+                                 vaddr, paddr, length, type);
+            if (ret < 0) {
+                return -1;
+            }
+        }
+
+        if ((kernel_base_in_block && kernel_base == block->offset) ||
+            (page_offset_in_block && page_offset == block->offset)) {
+            continue;
+        }
+
+        if (kernel_base_in_block && page_offset_in_block) {
+            if (page_offset >= kernel_base) {
+                length = kernel_base - block->offset;
+            } else {
+                length = page_offset - block->offset;
+            }
+        } else if (kernel_base_in_block) {
+            length = kernel_base - block->offset;
+        } else if (page_offset_in_block) {
+            length = page_offset - block->offset;
+        }
+        paddr = block->offset;
+        vaddr = 0;
+        ret = write_elf_load(mon, fd, block, phdr_index++, &offset,
+                             vaddr, paddr, length, type);
+        if (ret < 0) {
+            return -1;
+        }
+    }
+#endif
+
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+#if defined(TARGET_I386)
+        if (kernel_base >= block->offset &&
+            kernel_base < (block->offset + block->length)) {
+            continue;
+        }
+        if (page_offset >= block->offset &&
+            page_offset < (block->offset + block->length)) {
+            continue;
+        }
+#endif
+
+        if (type == 1) {
+            ret = write_elf64_load(mon, fd, block, phdr_index++, &offset, 0);
+        } else {
+            ret = write_elf32_load(mon, fd, block, phdr_index++, &offset, 0);
+        }
+        if (ret < 0) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+int do_dump(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+    const char *file = qdict_get_str(qdict, "file");
+    const char *p;
+    int fd = -1;
+
+#if !defined(WIN32)
+    if (strstart(file, "fd:", &p)) {
+        fd = monitor_get_fd(mon, p);
+        if (fd == -1) {
+            monitor_printf(mon, "dump: invalid file descriptor"
+                           " identifier\n");
+            return -1;
+        }
+    }
+#endif
+
+    if  (strstart(file, "file:", &p)) {
+        fd = open(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
+        if (fd < 0) {
+            monitor_printf(mon, "dump: failed to open %s\n", p);
+            return -1;
+        }
+    }
+
+    if (fd == -1) {
+        monitor_printf(mon, "unknown dump protocol: %s\n", file);
+        return -1;
+    }
+
+    vm_stop(RUN_STATE_PAUSED);
+    if (create_vmcore(mon, fd) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/dump.h b/dump.h
new file mode 100644
index 0000000..c91fa2c
--- /dev/null
+++ b/dump.h
@@ -0,0 +1,6 @@
+#ifndef DUMP_H
+#define DUMP_H
+
+int do_dump(Monitor *mon, const QDict *qdict, QObject **ret_data);
+
+#endif
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 089c1ac..ebbce8c 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -772,6 +772,22 @@ Migrate to @var{uri} (using -d to not wait for completion).
 ETEXI
 
     {
+        .name       = "dump",
+        .args_type  = "file:s",
+        .params     = "file",
+        .help       = "dump to file",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = do_dump,
+    },
+
+
+STEXI
+@item dump @var{file}
+@findex dump
+Dump to @var{file}.
+ETEXI
+
+    {
         .name       = "migrate_cancel",
         .args_type  = "",
         .params     = "",
diff --git a/monitor.c b/monitor.c
index 1be222e..9f26c3d 100644
--- a/monitor.c
+++ b/monitor.c
@@ -73,6 +73,9 @@
 #endif
 #include "hw/lm32_pic.h"
 
+/* for dump */
+#include "dump.h"
+
 //#define DEBUG
 //#define DEBUG_COMPLETION
 
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 97975a5..5cf21c5 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -485,6 +485,30 @@ Notes:
 EQMP
 
     {
+        .name       = "dump",
+        .args_type  = "file:s",
+        .params     = "file",
+        .help       = "dump to file",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = do_dump,
+    },
+
+SQMP
+dump
+-------
+
+Dump to file.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "dump", "arguments": { "file": "fd:dump" } }
+<- { "return": {} }
+
+EQMP
+
+    {
         .name       = "migrate_cancel",
         .args_type  = "",
         .params     = "",
-- 
1.7.1

      parent reply	other threads:[~2011-11-29  5:39 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-11-16  8:27 [Qemu-devel] [RFC] dump memory when host pci device is used by guest Wen Congyang
2011-11-16 16:29 ` Dave Anderson
2011-11-18 12:46   ` Jan Kiszka
2011-11-21  8:06     ` Wen Congyang
2011-11-26 10:27       ` Jan Kiszka
2011-11-26 21:45         ` Sergio Durigan Junior
2011-11-29  5:41 ` Wen Congyang [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4ED470A2.8090407@cn.fujitsu.com \
    --to=wency@cn.fujitsu.com \
    --cc=anderson@redhat.com \
    --cc=jan.kiszka@siemens.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.