From bdb3daaeb2743a14df2cab364622e2f47ae25093 Mon Sep 17 00:00:00 2001
From: Wen Congyang <wency@cn.fujitsu.com>
Date: Wed, 16 Nov 2011 16:06:10 +0800
Subject: [PATCH] dump sample

---
 Makefile.target |    1 +
 dump.c          |  377 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 dump.h          |    1 +
 hmp-commands.hx |   16 +++
 monitor.c       |    3 +
 qmp-commands.hx |   24 ++++
 6 files changed, 422 insertions(+), 0 deletions(-)
 create mode 100644 dump.c
 create mode 100644 dump.h

diff --git a/Makefile.target b/Makefile.target
index a111521..95d48a5 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -84,6 +84,7 @@ libobj-y += cpu_init.o
 endif
 libobj-$(TARGET_SPARC) += int32_helper.o
 libobj-$(TARGET_SPARC64) += int64_helper.o
+libobj-y += dump.o
 
 libobj-y += disas.o
 libobj-$(CONFIG_TCI_DIS) += tci-dis.o
diff --git a/dump.c b/dump.c
new file mode 100644
index 0000000..bdec246
--- /dev/null
+++ b/dump.c
@@ -0,0 +1,377 @@
+/*
+ * QEMU live dump
+ *
+ * Copyright Fujitsu, Corp. 2011
+ *
+ * Authors:
+ *
+ * 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"
+
+#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 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 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;
+
+    /* FIXME */
+    regs.orig_rax = 0;
+    regs.cs = 0;
+    regs.ss = 0;
+    regs.fs_base = 0;
+    regs.gs_base = 0;
+    regs.ds = 0;
+    regs.es = 0;
+    regs.fs = 0;
+    regs.gs = 0;
+
+    descsz = sizeof(prstatus_t) - sizeof(elf_gregset_t) +
+             sizeof(x86_64_user_regs_struct);
+    note_size = ((sizeof(Elf64_Nhdr) + 3) / 4 + (name_size + 3) / 4 +
+                (descsz +3) / 4) * 4;
+    note = g_malloc(note_size);
+
+    note->n_namesz = name_size;
+    note->n_descsz = descsz;
+    note->n_type = NT_PRSTATUS;
+    buf = (char *)note;
+    buf += ((sizeof(Elf64_Nhdr) + 3) / 4) * 4;
+    memcpy(buf, name, name_size);
+    buf += ((name_size + 3) / 4) * 4;
+    buf += descsz - sizeof(x86_64_user_regs_struct) - sizeof(int);
+    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
+
+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 write_elf32_note(Monitor *mon, int fd, CPUState *env,
+                            target_phys_addr_t *offset)
+{
+    /* TODO */
+    return 0;
+}
+
+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;
+}
+
+static int write_elf_header(Monitor *mon, int fd, int phdr_num, bool lma)
+{
+    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] = ELFDATA2LSB; /* FIXME */
+    elf_header.e_ident[EI_VERSION] = EV_CURRENT;
+    elf_header.e_type = ET_CORE;
+    elf_header.e_machine = lma ? EM_X86_64: EM_386;
+    elf_header.e_version = EV_CURRENT;
+    elf_header.e_ehsize = sizeof(elf_header);
+    elf_header.e_phoff = sizeof(Elf64_Ehdr);
+    elf_header.e_phentsize = sizeof(Elf64_Phdr);
+    elf_header.e_phnum = 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_elf_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 = PT_LOAD;
+    phdr.p_offset = *offset;
+    phdr.p_paddr = block->offset;
+    phdr.p_filesz = block->length;
+    phdr.p_memsz = block->length;
+    phdr.p_vaddr = 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_elf_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) {
+#ifdef TARGET_X86_64
+        if (lma) {
+            ret = write_elf64_note(mon, fd, env, offset);
+        } else {
+#endif
+            ret = write_elf32_note(mon, fd, env, offset);
+#ifdef TARGET_X86_64
+        }
+#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 = PT_NOTE;
+    phdr.p_offset = begin;
+    phdr.p_paddr = 0;
+    phdr.p_filesz = *offset - begin;
+    phdr.p_memsz = *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 create_vmcore(Monitor *mon, int fd)
+{
+    CPUState *env;
+    target_ulong kernel_base = -1, base_vaddr;
+    target_phys_addr_t offset;
+    int phdr_num, phdr_index;
+    RAMBlock *block;
+    bool lma = false;
+    int ret;
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        cpu_synchronize_state(env);
+    }
+
+#ifdef TARGET_X86_64
+    lma = !!(first_cpu->hflags & HF_LMA_MASK);
+    if (lma) {
+        kernel_base = get_phys_base_addr(first_cpu, &base_vaddr);
+        if (kernel_base == -1) {
+            monitor_printf(mon, "fd_dump: can not get phys_base\n");
+            return -1;
+        }
+    }
+#endif
+
+    phdr_num = 1; /* PT_NOTE */
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+        if (lma && kernel_base > block->offset &&
+            kernel_base < (block->offset + block->length)) {
+            phdr_num++;
+        }
+        phdr_num++;
+    }
+
+    ret = write_elf_header(mon, fd, phdr_num, lma);
+    if (ret < 0)
+        return -1;
+
+    phdr_index = 0;
+    offset = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) * phdr_num;
+
+    ret = write_elf_notes(mon, fd, phdr_index++, &offset, lma);
+    if (ret < 0)
+        return -1;
+
+#ifdef TARGET_X86_64
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+        if (lma && kernel_base >= block->offset &&
+            kernel_base < (block->offset + block->length)) {
+            if (kernel_base > block->offset) {
+                RAMBlock temp_block;
+
+                temp_block.host = block->host + (kernel_base-block->offset);
+                temp_block.offset = kernel_base;
+                temp_block.length = block->length - (kernel_base-block->offset);
+                ret = write_elf_load(mon, fd, &temp_block, phdr_index++, &offset,
+                                     base_vaddr);
+                if (ret < 0)
+                    return -1;
+
+                temp_block.host = block->host;
+                temp_block.offset = block->offset;
+                temp_block.length = kernel_base-block->offset;
+                ret = write_elf_load(mon, fd, &temp_block, phdr_index++,
+                                     &offset, 0);
+                if (ret < 0)
+                    return -1;
+            } else {
+                ret = write_elf_load(mon, fd, block, phdr_index++, &offset,
+                                     base_vaddr);
+                if (ret < 0)
+                    return -1;
+            }
+            break;
+        }
+    }
+#endif
+
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+        if (lma && kernel_base >= block->offset &&
+            kernel_base < (block->offset + block->length)) {
+            continue;
+        }
+
+        ret = write_elf_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, "fd_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, "fd_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..b5d9eb4
--- /dev/null
+++ b/dump.h
@@ -0,0 +1 @@
+int do_dump(Monitor *mon, const QDict *qdict, QObject **ret_data);
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 5ea35de..5df35e0 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

