>From bdb3daaeb2743a14df2cab364622e2f47ae25093 Mon Sep 17 00:00:00 2001
From: Wen Congyang
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
+#include
+#include
+#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, ®s, 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
address@hidden dump @var{file}
address@hidden 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