Patchwork [RFC,09/14,v7] introduce a new monitor command 'dump' to dump guest's memory

login
register
mail settings
Submitter Wen Congyang
Date March 1, 2012, 2:51 a.m.
Message ID <4F4EE43E.3020309@cn.fujitsu.com>
Download mbox | patch
Permalink /patch/143884/
State New
Headers show

Comments

Wen Congyang - March 1, 2012, 2:51 a.m.
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
---
 Makefile.target  |    2 +-
 dump.c           |  637 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hmp-commands.hx  |   20 ++
 hmp.c            |    9 +
 hmp.h            |    1 +
 qapi-schema.json |   13 ++
 qmp-commands.hx  |   29 +++
 7 files changed, 710 insertions(+), 1 deletions(-)
 create mode 100644 dump.c
HATAYAMA Daisuke - March 1, 2012, 7:04 a.m.
From: Wen Congyang <wency@cn.fujitsu.com>
Subject: [RFC][PATCH 09/14 v7] introduce a new monitor command 'dump' to dump guest's memory
Date: Thu, 01 Mar 2012 10:51:42 +0800

> +    /*
> +     * calculate phdr_num
> +     *
> +     * the type of phdr->num is uint16_t, so we should avoid overflow
> +     */
> +    s->phdr_num = 1; /* PT_NOTE */
> +    if (s->list.num > (1 << 16) - 2) {
> +        s->phdr_num = (1 << 16) - 1;
> +    } else {
> +        s->phdr_num += s->list.num;
> +    }
> +
> +    return s;
> +}

Though e_phnum is uint16_t at default, there's extension up to
uint32_t. Look at relatively new manual page. This is from FC14's.

     e_phnum     This member  holds the number of  entries in the
                 program  header  table.   Thus  the  product  of
                 e_phentsize and  e_phnum gives the  table's size
                 in  bytes.  If  a  file has  no program  header,
                 e_phnum holds the value zero.

                 If the  number of entries in  the program header
                 table  is  larger   than  or  equal  to  PN_XNUM
                 (0xffff), this member holds PN_XNUM (0xffff) and
                 the real number of entries in the program header
                 table  is  held in  the  sh_info  member of  the
                 initial   entry   in   section   header   table.
                 Otherwise,  the sh_info  member  of the  initial
                 entry contains the value zero.

                 PN_XNUM  This is defined  as 0xffff, the largest
                          number  e_phnum  can  have,  specifying
                          where  the  actual  number  of  program
                          headers is assigned.

Recent kernel, gdb and tools in binutils supports this. But crash
doesn't, so you need to fix this.

I'm interested in the number of program headers at worst
case. According to Intel Programming Guide 3A, Table 4-1. shows
physical-address width on IA-32e is up to 52 and linear-address width
is 48. Can the number exceed this limit in theory? Also how many
program headers are created typically?

Thanks.
HATAYAMA, Daisuke
Wen Congyang - March 1, 2012, 7:21 a.m.
At 03/01/2012 03:04 PM, HATAYAMA Daisuke Wrote:
> From: Wen Congyang <wency@cn.fujitsu.com>
> Subject: [RFC][PATCH 09/14 v7] introduce a new monitor command 'dump' to dump guest's memory
> Date: Thu, 01 Mar 2012 10:51:42 +0800
> 
>> +    /*
>> +     * calculate phdr_num
>> +     *
>> +     * the type of phdr->num is uint16_t, so we should avoid overflow
>> +     */
>> +    s->phdr_num = 1; /* PT_NOTE */
>> +    if (s->list.num > (1 << 16) - 2) {
>> +        s->phdr_num = (1 << 16) - 1;
>> +    } else {
>> +        s->phdr_num += s->list.num;
>> +    }
>> +
>> +    return s;
>> +}
> 
> Though e_phnum is uint16_t at default, there's extension up to
> uint32_t. Look at relatively new manual page. This is from FC14's.
> 
>      e_phnum     This member  holds the number of  entries in the
>                  program  header  table.   Thus  the  product  of
>                  e_phentsize and  e_phnum gives the  table's size
>                  in  bytes.  If  a  file has  no program  header,
>                  e_phnum holds the value zero.
> 
>                  If the  number of entries in  the program header
>                  table  is  larger   than  or  equal  to  PN_XNUM
>                  (0xffff), this member holds PN_XNUM (0xffff) and
>                  the real number of entries in the program header
>                  table  is  held in  the  sh_info  member of  the
>                  initial   entry   in   section   header   table.
>                  Otherwise,  the sh_info  member  of the  initial
>                  entry contains the value zero.
> 
>                  PN_XNUM  This is defined  as 0xffff, the largest
>                           number  e_phnum  can  have,  specifying
>                           where  the  actual  number  of  program
>                           headers is assigned.

Good news.

> 
> Recent kernel, gdb and tools in binutils supports this. But crash
> doesn't, so you need to fix this.

I think it can be easily fixed.

> 
> I'm interested in the number of program headers at worst
> case. According to Intel Programming Guide 3A, Table 4-1. shows
> physical-address width on IA-32e is up to 52 and linear-address width
> is 48. Can the number exceed this limit in theory? Also how many
> program headers are created typically?

In my test, the guest has 512M memory, and it contains about 1000~2000 program
headers.

In theory, if the guest has 2^52 memory, the number can still exceed this limit.
Tha maxnium number is 2^52/2^12

Thanks
Wen Congyang

> 
> Thanks.
> HATAYAMA, Daisuke
> 
>

Patch

diff --git a/Makefile.target b/Makefile.target
index cfd3113..4ae59f5 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -210,7 +210,7 @@  obj-$(CONFIG_NO_KVM) += kvm-stub.o
 obj-$(CONFIG_VGA) += vga.o
 obj-y += memory.o savevm.o
 obj-y += memory_mapping.o
-obj-$(CONFIG_HAVE_CORE_DUMP) += arch_dump.o
+obj-$(CONFIG_HAVE_CORE_DUMP) += arch_dump.o dump.o
 LIBS+=-lz
 
 obj-i386-$(CONFIG_KVM) += hyperv.o
diff --git a/dump.c b/dump.c
new file mode 100644
index 0000000..c9baedb
--- /dev/null
+++ b/dump.c
@@ -0,0 +1,637 @@ 
+/*
+ * QEMU 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 <glib.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"
+#include "memory_mapping.h"
+#include "error.h"
+#include "qmp-commands.h"
+#include "gdbstub.h"
+
+static inline uint16_t cpu_contert_to_target16(uint16_t val, int endian)
+{
+    if (endian == ELFDATA2LSB) {
+        val = cpu_to_le16(val);
+    } else {
+        val = cpu_to_be16(val);
+    }
+
+    return val;
+}
+
+static inline uint32_t cpu_contert_to_target32(uint32_t val, int endian)
+{
+    if (endian == ELFDATA2LSB) {
+        val = cpu_to_le32(val);
+    } else {
+        val = cpu_to_be32(val);
+    }
+
+    return val;
+}
+
+static inline uint64_t cpu_contert_to_target64(uint64_t val, int endian)
+{
+    if (endian == ELFDATA2LSB) {
+        val = cpu_to_le64(val);
+    } else {
+        val = cpu_to_be64(val);
+    }
+
+    return val;
+}
+
+enum {
+    DUMP_STATE_ERROR,
+    DUMP_STATE_SETUP,
+    DUMP_STATE_CANCELLED,
+    DUMP_STATE_ACTIVE,
+    DUMP_STATE_COMPLETED,
+};
+
+typedef struct DumpState {
+    ArchDumpInfo dump_info;
+    MemoryMappingList list;
+    int phdr_num;
+    int state;
+    bool resume;
+    char *error;
+    target_phys_addr_t memory_offset;
+    write_core_dump_function f;
+    void (*cleanup)(void *opaque);
+    void *opaque;
+} DumpState;
+
+static DumpState *dump_get_current(void)
+{
+    static DumpState current_dump = {
+        .state = DUMP_STATE_SETUP,
+    };
+
+    return &current_dump;
+}
+
+static int dump_cleanup(DumpState *s)
+{
+    int ret = 0;
+
+    memory_mapping_list_free(&s->list);
+    s->cleanup(s->opaque);
+    if (s->resume) {
+        vm_start();
+    }
+
+    return ret;
+}
+
+static void dump_error(DumpState *s, const char *reason)
+{
+    s->state = DUMP_STATE_ERROR;
+    s->error = g_strdup(reason);
+    dump_cleanup(s);
+}
+
+static int write_elf64_header(DumpState *s)
+{
+    Elf64_Ehdr elf_header;
+    int ret;
+    int endian = s->dump_info.d_endian;
+
+    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] = s->dump_info.d_endian;
+    elf_header.e_ident[EI_VERSION] = EV_CURRENT;
+    elf_header.e_type = cpu_contert_to_target16(ET_CORE, endian);
+    elf_header.e_machine = cpu_contert_to_target16(s->dump_info.d_machine,
+                                                   endian);
+    elf_header.e_version = cpu_contert_to_target32(EV_CURRENT, endian);
+    elf_header.e_ehsize = cpu_contert_to_target16(sizeof(elf_header), endian);
+    elf_header.e_phoff = cpu_contert_to_target64(sizeof(Elf64_Ehdr), endian);
+    elf_header.e_phentsize = cpu_contert_to_target16(sizeof(Elf64_Phdr), endian);
+    elf_header.e_phnum = cpu_contert_to_target16(s->phdr_num, endian);
+
+    ret = s->f(0, &elf_header, sizeof(elf_header), s->opaque);
+    if (ret < 0) {
+        dump_error(s, "dump: failed to write elf header.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_elf32_header(DumpState *s)
+{
+    Elf32_Ehdr elf_header;
+    int ret;
+    int endian = s->dump_info.d_endian;
+
+    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_contert_to_target16(ET_CORE, endian);
+    elf_header.e_machine = cpu_contert_to_target16(s->dump_info.d_machine,
+                                                   endian);
+    elf_header.e_version = cpu_contert_to_target32(EV_CURRENT, endian);
+    elf_header.e_ehsize = cpu_contert_to_target16(sizeof(elf_header), endian);
+    elf_header.e_phoff = cpu_contert_to_target32(sizeof(Elf32_Ehdr), endian);
+    elf_header.e_phentsize = cpu_contert_to_target16(sizeof(Elf32_Phdr),
+                                                     endian);
+    elf_header.e_phnum = cpu_contert_to_target16(s->phdr_num, endian);
+
+    ret = s->f(0, &elf_header, sizeof(elf_header), s->opaque);
+    if (ret < 0) {
+        dump_error(s, "dump: failed to write elf header.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_elf64_load(DumpState *s, MemoryMapping *memory_mapping,
+                            int phdr_index, target_phys_addr_t offset)
+{
+    Elf64_Phdr phdr;
+    off_t phdr_offset;
+    int ret;
+    int endian = s->dump_info.d_endian;
+
+    memset(&phdr, 0, sizeof(Elf64_Phdr));
+    phdr.p_type = cpu_contert_to_target32(PT_LOAD, endian);
+    phdr.p_offset = cpu_contert_to_target64(offset, endian);
+    phdr.p_paddr = cpu_contert_to_target64(memory_mapping->phys_addr, endian);
+    if (offset == -1) {
+        phdr.p_filesz = 0;
+    } else {
+        phdr.p_filesz = cpu_contert_to_target64(memory_mapping->length, endian);
+    }
+    phdr.p_memsz = cpu_contert_to_target64(memory_mapping->length, endian);
+    phdr.p_vaddr = cpu_contert_to_target64(memory_mapping->virt_addr, endian);
+
+    phdr_offset = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr)*phdr_index;
+    ret = s->f(phdr_offset, &phdr, sizeof(Elf64_Phdr), s->opaque);
+    if (ret < 0) {
+        dump_error(s, "dump: failed to write program header table.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_elf32_load(DumpState *s, MemoryMapping *memory_mapping,
+                            int phdr_index, target_phys_addr_t offset)
+{
+    Elf32_Phdr phdr;
+    off_t phdr_offset;
+    int ret;
+    int endian = s->dump_info.d_endian;
+
+    memset(&phdr, 0, sizeof(Elf32_Phdr));
+    phdr.p_type = cpu_contert_to_target32(PT_LOAD, endian);
+    phdr.p_offset = cpu_contert_to_target32(offset, endian);
+    phdr.p_paddr = cpu_contert_to_target32(memory_mapping->phys_addr, endian);
+    if (offset == -1) {
+        phdr.p_filesz = 0;
+    } else {
+        phdr.p_filesz = cpu_contert_to_target32(memory_mapping->length, endian);
+    }
+    phdr.p_memsz = cpu_contert_to_target32(memory_mapping->length, endian);
+    phdr.p_vaddr = cpu_contert_to_target32(memory_mapping->virt_addr, endian);
+
+    phdr_offset = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr)*phdr_index;
+    ret = s->f(phdr_offset, &phdr, sizeof(Elf32_Phdr), s->opaque);
+    if (ret < 0) {
+        dump_error(s, "dump: failed to write program header table.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_elf64_notes(DumpState *s, int phdr_index,
+                             target_phys_addr_t *offset)
+{
+    CPUState *env;
+    int ret;
+    target_phys_addr_t begin = *offset;
+    Elf64_Phdr phdr;
+    off_t phdr_offset;
+    int id;
+    int endian = s->dump_info.d_endian;
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        id = gdb_id(env);
+        ret = cpu_write_elf64_note(s->f, env, id, offset, s->opaque);
+        if (ret < 0) {
+            dump_error(s, "dump: failed to write elf notes.\n");
+            return -1;
+        }
+    }
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        ret = cpu_write_elf64_qemunote(s->f, env, offset, s->opaque);
+        if (ret < 0) {
+            dump_error(s, "dump: failed to write CPU status.\n");
+            return -1;
+        }
+    }
+
+    memset(&phdr, 0, sizeof(Elf64_Phdr));
+    phdr.p_type = cpu_contert_to_target32(PT_NOTE, endian);
+    phdr.p_offset = cpu_contert_to_target64(begin, endian);
+    phdr.p_paddr = 0;
+    phdr.p_filesz = cpu_contert_to_target64(*offset - begin, endian);
+    phdr.p_memsz = cpu_contert_to_target64(*offset - begin, endian);
+    phdr.p_vaddr = 0;
+
+    phdr_offset = sizeof(Elf64_Ehdr);
+    ret = s->f(phdr_offset, &phdr, sizeof(Elf64_Phdr), s->opaque);
+    if (ret < 0) {
+        dump_error(s, "dump: failed to write program header table.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_elf32_notes(DumpState *s, 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;
+    int id;
+    int endian = s->dump_info.d_endian;
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        id = gdb_id(env);
+        ret = cpu_write_elf32_note(s->f, env, id, offset, s->opaque);
+        if (ret < 0) {
+            dump_error(s, "dump: failed to write elf notes.\n");
+            return -1;
+        }
+    }
+
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        ret = cpu_write_elf32_qemunote(s->f, env, offset, s->opaque);
+        if (ret < 0) {
+            dump_error(s, "dump: failed to write CPU status.\n");
+            return -1;
+        }
+    }
+
+    memset(&phdr, 0, sizeof(Elf32_Phdr));
+    phdr.p_type = cpu_contert_to_target32(PT_NOTE, endian);
+    phdr.p_offset = cpu_contert_to_target32(begin, endian);
+    phdr.p_paddr = 0;
+    phdr.p_filesz = cpu_contert_to_target32(*offset - begin, endian);
+    phdr.p_memsz = cpu_contert_to_target32(*offset - begin, endian);
+    phdr.p_vaddr = 0;
+
+    phdr_offset = sizeof(Elf32_Ehdr);
+    ret = s->f(phdr_offset, &phdr, sizeof(Elf32_Phdr), s->opaque);
+    if (ret < 0) {
+        dump_error(s, "dump: failed to write program header table.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_data(DumpState *s, void *buf, int length,
+                      target_phys_addr_t *offset)
+{
+    int ret;
+
+    ret = s->f(*offset, buf, length, s->opaque);
+    if (ret < 0) {
+        dump_error(s, "dump: failed to save memory.\n");
+        return -1;
+    }
+
+    *offset += length;
+    return 0;
+}
+
+/* write the memroy to vmcore. 1 page per I/O. */
+static int write_memory(DumpState *s, RAMBlock *block,
+                        target_phys_addr_t *offset)
+{
+    int i, ret;
+
+    for (i = 0; i < block->length / TARGET_PAGE_SIZE; i++) {
+        ret = write_data(s, block->host + i * TARGET_PAGE_SIZE,
+                         TARGET_PAGE_SIZE, offset);
+        if (ret < 0) {
+            return -1;
+        }
+    }
+
+    if ((block->length % TARGET_PAGE_SIZE) != 0) {
+        ret = write_data(s, block->host + i * TARGET_PAGE_SIZE,
+                         block->length % TARGET_PAGE_SIZE, offset);
+        if (ret < 0) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+/* get the memory's offset in the vmcore */
+static target_phys_addr_t get_offset(target_phys_addr_t phys_addr,
+                                     target_phys_addr_t memory_offset)
+{
+    RAMBlock *block;
+    target_phys_addr_t offset = memory_offset;
+
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+        if (phys_addr >= block->offset &&
+            phys_addr < block->offset + block->length) {
+            return phys_addr - block->offset + offset;
+        }
+        offset += block->length;
+    }
+
+    return -1;
+}
+
+/* write elf header, PT_NOTE and elf note to vmcore. */
+static int dump_begin(DumpState *s)
+{
+    target_phys_addr_t offset;
+    int ret;
+
+    s->state = DUMP_STATE_ACTIVE;
+
+    /*
+     * the vmcore's format is:
+     *   --------------
+     *   |  elf header |
+     *   --------------
+     *   |  PT_NOTE    |
+     *   --------------
+     *   |  PT_LOAD    |
+     *   --------------
+     *   |  ......     |
+     *   --------------
+     *   |  PT_LOAD    |
+     *   --------------
+     *   |  elf note   |
+     *   --------------
+     *   |  memory     |
+     *   --------------
+     *
+     * we only know where the memory is saved after we write elf note into
+     * vmcore.
+     */
+
+    /* write elf header to vmcore */
+    if (s->dump_info.d_class == ELFCLASS64) {
+        ret = write_elf64_header(s);
+    } else {
+        ret = write_elf32_header(s);
+    }
+    if (ret < 0) {
+        return -1;
+    }
+
+    /* write elf notes to vmcore */
+    if (s->dump_info.d_class == ELFCLASS64) {
+        offset = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr)*s->phdr_num;
+        ret = write_elf64_notes(s, 0, &offset);
+    } else {
+        offset = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr)*s->phdr_num;
+        ret = write_elf32_notes(s, 0, &offset);
+    }
+
+    if (ret < 0) {
+        return -1;
+    }
+
+    s->memory_offset = offset;
+    return 0;
+}
+
+/* write PT_LOAD to vmcore */
+static int dump_completed(DumpState *s)
+{
+    target_phys_addr_t offset;
+    MemoryMapping *memory_mapping;
+    int phdr_index = 1, ret;
+
+    QTAILQ_FOREACH(memory_mapping, &s->list.head, next) {
+        offset = get_offset(memory_mapping->phys_addr, s->memory_offset);
+        if (s->dump_info.d_class == ELFCLASS64) {
+            ret = write_elf64_load(s, memory_mapping, phdr_index++, offset);
+        } else {
+            ret = write_elf32_load(s, memory_mapping, phdr_index++, offset);
+        }
+        if (ret < 0) {
+            return -1;
+        }
+    }
+
+    s->state = DUMP_STATE_COMPLETED;
+    dump_cleanup(s);
+    return 0;
+}
+
+/* write all memory to vmcore */
+static int dump_iterate(DumpState *s)
+{
+    RAMBlock *block;
+    target_phys_addr_t offset = s->memory_offset;
+    int ret;
+
+    /* write all memory to vmcore */
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+        ret = write_memory(s, block, &offset);
+        if (ret < 0) {
+            return -1;
+        }
+    }
+
+    return dump_completed(s);
+}
+
+static int create_vmcore(DumpState *s)
+{
+    int ret;
+
+    ret = dump_begin(s);
+    if (ret < 0) {
+        return -1;
+    }
+
+    ret = dump_iterate(s);
+    if (ret < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static DumpState *dump_init(Error **errp)
+{
+    CPUState *env;
+    DumpState *s = dump_get_current();
+    int ret;
+
+    if (runstate_is_running()) {
+        vm_stop(RUN_STATE_PAUSED);
+        s->resume = true;
+    } else {
+        s->resume = false;
+    }
+    s->state = DUMP_STATE_SETUP;
+    if (s->error) {
+        g_free(s->error);
+        s->error = NULL;
+    }
+
+    /*
+     * get dump info: endian, class and architecture.
+     * If the target architecture is not supported, cpu_get_dump_info() will
+     * return -1.
+     *
+     * if we use kvm, we should synchronize the register before we get dump
+     * info.
+     */
+    for (env = first_cpu; env != NULL; env = env->next_cpu) {
+        cpu_synchronize_state(env);
+    }
+
+    ret = cpu_get_dump_info(&s->dump_info);
+    if (ret < 0) {
+        error_set(errp, QERR_UNSUPPORTED);
+        return NULL;
+    }
+
+    /* get memory mapping */
+    memory_mapping_list_init(&s->list);
+    qemu_get_guest_memory_mapping(&s->list);
+
+    /*
+     * calculate phdr_num
+     *
+     * the type of phdr->num is uint16_t, so we should avoid overflow
+     */
+    s->phdr_num = 1; /* PT_NOTE */
+    if (s->list.num > (1 << 16) - 2) {
+        s->phdr_num = (1 << 16) - 1;
+    } else {
+        s->phdr_num += s->list.num;
+    }
+
+    return s;
+}
+
+static int fd_write_vmcore(target_phys_addr_t offset, void *buf, size_t size,
+                           void *opaque)
+{
+    int fd = (int)(intptr_t)opaque;
+    int ret;
+
+    ret = lseek(fd, offset, SEEK_SET);
+    if (ret < 0) {
+        return -1;
+    }
+
+    ret = write(fd, buf, size);
+    if (ret != size) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static void fd_cleanup(void *opaque)
+{
+    int fd = (int)(intptr_t)opaque;
+
+    if (fd != -1) {
+        close(fd);
+    }
+}
+
+static DumpState *dump_init_fd(int fd, Error **errp)
+{
+    DumpState *s = dump_init(errp);
+
+    if (s == NULL) {
+        return NULL;
+    }
+
+    s->f = fd_write_vmcore;
+    s->cleanup = fd_cleanup;
+    s->opaque = (void *)(intptr_t)fd;
+
+    return s;
+}
+
+void qmp_dump(const char *file, Error **errp)
+{
+    const char *p;
+    int fd = -1;
+    DumpState *s;
+
+#if !defined(WIN32)
+    if (strstart(file, "fd:", &p)) {
+        fd = qemu_get_fd(p);
+        if (fd == -1) {
+            error_set(errp, QERR_FD_NOT_FOUND, p);
+            return;
+        }
+    }
+#endif
+
+    if  (strstart(file, "file:", &p)) {
+        fd = open(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR);
+        if (fd < 0) {
+            error_set(errp, QERR_OPEN_FILE_FAILED, p);
+            return;
+        }
+    }
+
+    if (fd == -1) {
+        error_set(errp, QERR_INVALID_PARAMETER, "file");
+        return;
+    }
+
+    s = dump_init_fd(fd, errp);
+    if (!s) {
+        return;
+    }
+
+    if (create_vmcore(s) < 0) {
+        error_set(errp, QERR_IO_ERROR);
+    }
+
+    return;
+}
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 64b3656..19200ad 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -880,6 +880,26 @@  server will ask the spice/vnc client to automatically reconnect using the
 new parameters (if specified) once the vm migration finished successfully.
 ETEXI
 
+#if defined(CONFIG_HAVE_CORE_DUMP)
+    {
+        .name       = "dump",
+        .args_type  = "file:s",
+        .params     = "file",
+        .help       = "dump to file",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd = hmp_dump,
+    },
+
+
+STEXI
+@item dump @var{file}
+@findex dump
+Dump to @var{file}. The file can be processed with crash or gdb.
+    file: destination file(started with "file:") or destination file descriptor
+          (started with "fd:")
+ETEXI
+#endif
+
     {
         .name       = "snapshot_blkdev",
         .args_type  = "device:B,snapshot-file:s?,format:s?",
diff --git a/hmp.c b/hmp.c
index 3a54455..309ccec 100644
--- a/hmp.c
+++ b/hmp.c
@@ -856,3 +856,12 @@  void hmp_block_job_cancel(Monitor *mon, const QDict *qdict)
 
     hmp_handle_error(mon, &error);
 }
+
+void hmp_dump(Monitor *mon, const QDict *qdict)
+{
+    Error *errp = NULL;
+    const char *file = qdict_get_str(qdict, "file");
+
+    qmp_dump(file, &errp);
+    hmp_handle_error(mon, &errp);
+}
diff --git a/hmp.h b/hmp.h
index 5409464..b055e50 100644
--- a/hmp.h
+++ b/hmp.h
@@ -59,5 +59,6 @@  void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict);
 void hmp_block_stream(Monitor *mon, const QDict *qdict);
 void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
 void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
+void hmp_dump(Monitor *mon, const QDict *qdict);
 
 #endif
diff --git a/qapi-schema.json b/qapi-schema.json
index d0b6792..3c4aa70 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1593,3 +1593,16 @@ 
 { 'command': 'qom-list-types',
   'data': { '*implements': 'str', '*abstract': 'bool' },
   'returns': [ 'ObjectTypeInfo' ] }
+
+##
+# @dump
+#
+# Dump guest's memory to vmcore.
+#
+# @file: the filename or file descriptor of the vmcore.
+#
+# Returns: nothing on success
+#
+# Since: 1.1
+##
+{ 'command': 'dump', 'data': { 'file': 'str' } }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 705f704..1199316 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -586,6 +586,35 @@  Example:
 
 EQMP
 
+#if defined(CONFIG_HAVE_CORE_DUMP)
+    {
+        .name       = "dump",
+        .args_type  = "file:s",
+        .params     = "file",
+        .help       = "dump to file",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = qmp_marshal_input_dump,
+    },
+
+SQMP
+dump
+
+
+Dump to file. The file can be processed with crash or gdb.
+
+Arguments:
+
+- "file": destination file(started with "file:") or destination file descriptor
+          (started with "fd:") (json-string)
+
+Example:
+
+-> { "execute": "dump", "arguments": { "file": "fd:dump" } }
+<- { "return": {} }
+
+EQMP
+#endif
+
     {
         .name       = "netdev_add",
         .args_type  = "netdev:O",