blob: 2b940bddcd3873d749f43d9c99c1db32d2e752c2 [file] [log] [blame]
Wen Congyang783e9b42012-05-07 12:10:47 +08001/*
2 * QEMU dump
3 *
4 * Copyright Fujitsu, Corp. 2011, 2012
5 *
6 * Authors:
7 * Wen Congyang <wency@cn.fujitsu.com>
8 *
Stefan Weil352666e2012-06-10 19:34:04 +00009 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
Wen Congyang783e9b42012-05-07 12:10:47 +080011 *
12 */
13
14#include "qemu-common.h"
Wen Congyang783e9b42012-05-07 12:10:47 +080015#include "elf.h"
Wen Congyang783e9b42012-05-07 12:10:47 +080016#include "cpu.h"
Paolo Bonzini022c62c2012-12-17 18:19:49 +010017#include "exec/cpu-all.h"
18#include "exec/hwaddr.h"
Paolo Bonzini83c90892012-12-17 18:19:49 +010019#include "monitor/monitor.h"
Paolo Bonzini9c17d612012-12-17 18:20:04 +010020#include "sysemu/kvm.h"
21#include "sysemu/dump.h"
22#include "sysemu/sysemu.h"
23#include "sysemu/memory_mapping.h"
Andreas Färber1b3509c2013-06-09 16:48:29 +020024#include "sysemu/cpus.h"
Paolo Bonzini7b1b5d12012-12-17 18:19:43 +010025#include "qapi/error.h"
Wen Congyang783e9b42012-05-07 12:10:47 +080026#include "qmp-commands.h"
Wen Congyang783e9b42012-05-07 12:10:47 +080027
Wen Congyang783e9b42012-05-07 12:10:47 +080028static uint16_t cpu_convert_to_target16(uint16_t val, int endian)
29{
30 if (endian == ELFDATA2LSB) {
31 val = cpu_to_le16(val);
32 } else {
33 val = cpu_to_be16(val);
34 }
35
36 return val;
37}
38
39static uint32_t cpu_convert_to_target32(uint32_t val, int endian)
40{
41 if (endian == ELFDATA2LSB) {
42 val = cpu_to_le32(val);
43 } else {
44 val = cpu_to_be32(val);
45 }
46
47 return val;
48}
49
50static uint64_t cpu_convert_to_target64(uint64_t val, int endian)
51{
52 if (endian == ELFDATA2LSB) {
53 val = cpu_to_le64(val);
54 } else {
55 val = cpu_to_be64(val);
56 }
57
58 return val;
59}
60
61typedef struct DumpState {
Laszlo Ersek5ee163e2013-08-06 12:37:09 +020062 GuestPhysBlockList guest_phys_blocks;
Wen Congyang783e9b42012-05-07 12:10:47 +080063 ArchDumpInfo dump_info;
64 MemoryMappingList list;
65 uint16_t phdr_num;
66 uint32_t sh_info;
67 bool have_section;
68 bool resume;
Aneesh Kumar K.Vbb6b6842013-10-01 21:49:32 +053069 ssize_t note_size;
Avi Kivitya8170e52012-10-23 12:30:10 +020070 hwaddr memory_offset;
Wen Congyang783e9b42012-05-07 12:10:47 +080071 int fd;
72
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +020073 GuestPhysBlock *next_block;
Wen Congyang783e9b42012-05-07 12:10:47 +080074 ram_addr_t start;
75 bool has_filter;
76 int64_t begin;
77 int64_t length;
78 Error **errp;
qiaonuohan4835ef72014-02-18 14:11:29 +080079
80 uint8_t *note_buf; /* buffer for notes */
81 size_t note_buf_offset; /* the writing place in note_buf */
Wen Congyang783e9b42012-05-07 12:10:47 +080082} DumpState;
83
84static int dump_cleanup(DumpState *s)
85{
86 int ret = 0;
87
Laszlo Ersek5ee163e2013-08-06 12:37:09 +020088 guest_phys_blocks_free(&s->guest_phys_blocks);
Wen Congyang783e9b42012-05-07 12:10:47 +080089 memory_mapping_list_free(&s->list);
90 if (s->fd != -1) {
91 close(s->fd);
92 }
93 if (s->resume) {
94 vm_start();
95 }
96
97 return ret;
98}
99
100static void dump_error(DumpState *s, const char *reason)
101{
102 dump_cleanup(s);
103}
104
qiaonuohanb5ba1cc2014-02-18 14:11:25 +0800105static int fd_write_vmcore(const void *buf, size_t size, void *opaque)
Wen Congyang783e9b42012-05-07 12:10:47 +0800106{
107 DumpState *s = opaque;
Luiz Capitulino2f616522012-09-21 13:17:55 -0300108 size_t written_size;
Wen Congyang783e9b42012-05-07 12:10:47 +0800109
Luiz Capitulino2f616522012-09-21 13:17:55 -0300110 written_size = qemu_write_full(s->fd, buf, size);
111 if (written_size != size) {
112 return -1;
Wen Congyang783e9b42012-05-07 12:10:47 +0800113 }
114
115 return 0;
116}
117
118static int write_elf64_header(DumpState *s)
119{
120 Elf64_Ehdr elf_header;
121 int ret;
122 int endian = s->dump_info.d_endian;
123
124 memset(&elf_header, 0, sizeof(Elf64_Ehdr));
125 memcpy(&elf_header, ELFMAG, SELFMAG);
126 elf_header.e_ident[EI_CLASS] = ELFCLASS64;
127 elf_header.e_ident[EI_DATA] = s->dump_info.d_endian;
128 elf_header.e_ident[EI_VERSION] = EV_CURRENT;
129 elf_header.e_type = cpu_convert_to_target16(ET_CORE, endian);
130 elf_header.e_machine = cpu_convert_to_target16(s->dump_info.d_machine,
131 endian);
132 elf_header.e_version = cpu_convert_to_target32(EV_CURRENT, endian);
133 elf_header.e_ehsize = cpu_convert_to_target16(sizeof(elf_header), endian);
134 elf_header.e_phoff = cpu_convert_to_target64(sizeof(Elf64_Ehdr), endian);
135 elf_header.e_phentsize = cpu_convert_to_target16(sizeof(Elf64_Phdr),
136 endian);
137 elf_header.e_phnum = cpu_convert_to_target16(s->phdr_num, endian);
138 if (s->have_section) {
139 uint64_t shoff = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) * s->sh_info;
140
141 elf_header.e_shoff = cpu_convert_to_target64(shoff, endian);
142 elf_header.e_shentsize = cpu_convert_to_target16(sizeof(Elf64_Shdr),
143 endian);
144 elf_header.e_shnum = cpu_convert_to_target16(1, endian);
145 }
146
147 ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
148 if (ret < 0) {
149 dump_error(s, "dump: failed to write elf header.\n");
150 return -1;
151 }
152
153 return 0;
154}
155
156static int write_elf32_header(DumpState *s)
157{
158 Elf32_Ehdr elf_header;
159 int ret;
160 int endian = s->dump_info.d_endian;
161
162 memset(&elf_header, 0, sizeof(Elf32_Ehdr));
163 memcpy(&elf_header, ELFMAG, SELFMAG);
164 elf_header.e_ident[EI_CLASS] = ELFCLASS32;
165 elf_header.e_ident[EI_DATA] = endian;
166 elf_header.e_ident[EI_VERSION] = EV_CURRENT;
167 elf_header.e_type = cpu_convert_to_target16(ET_CORE, endian);
168 elf_header.e_machine = cpu_convert_to_target16(s->dump_info.d_machine,
169 endian);
170 elf_header.e_version = cpu_convert_to_target32(EV_CURRENT, endian);
171 elf_header.e_ehsize = cpu_convert_to_target16(sizeof(elf_header), endian);
172 elf_header.e_phoff = cpu_convert_to_target32(sizeof(Elf32_Ehdr), endian);
173 elf_header.e_phentsize = cpu_convert_to_target16(sizeof(Elf32_Phdr),
174 endian);
175 elf_header.e_phnum = cpu_convert_to_target16(s->phdr_num, endian);
176 if (s->have_section) {
177 uint32_t shoff = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) * s->sh_info;
178
179 elf_header.e_shoff = cpu_convert_to_target32(shoff, endian);
180 elf_header.e_shentsize = cpu_convert_to_target16(sizeof(Elf32_Shdr),
181 endian);
182 elf_header.e_shnum = cpu_convert_to_target16(1, endian);
183 }
184
185 ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
186 if (ret < 0) {
187 dump_error(s, "dump: failed to write elf header.\n");
188 return -1;
189 }
190
191 return 0;
192}
193
194static int write_elf64_load(DumpState *s, MemoryMapping *memory_mapping,
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200195 int phdr_index, hwaddr offset,
196 hwaddr filesz)
Wen Congyang783e9b42012-05-07 12:10:47 +0800197{
198 Elf64_Phdr phdr;
199 int ret;
200 int endian = s->dump_info.d_endian;
201
202 memset(&phdr, 0, sizeof(Elf64_Phdr));
203 phdr.p_type = cpu_convert_to_target32(PT_LOAD, endian);
204 phdr.p_offset = cpu_convert_to_target64(offset, endian);
205 phdr.p_paddr = cpu_convert_to_target64(memory_mapping->phys_addr, endian);
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200206 phdr.p_filesz = cpu_convert_to_target64(filesz, endian);
Wen Congyang783e9b42012-05-07 12:10:47 +0800207 phdr.p_memsz = cpu_convert_to_target64(memory_mapping->length, endian);
208 phdr.p_vaddr = cpu_convert_to_target64(memory_mapping->virt_addr, endian);
209
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200210 assert(memory_mapping->length >= filesz);
211
Wen Congyang783e9b42012-05-07 12:10:47 +0800212 ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
213 if (ret < 0) {
214 dump_error(s, "dump: failed to write program header table.\n");
215 return -1;
216 }
217
218 return 0;
219}
220
221static int write_elf32_load(DumpState *s, MemoryMapping *memory_mapping,
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200222 int phdr_index, hwaddr offset,
223 hwaddr filesz)
Wen Congyang783e9b42012-05-07 12:10:47 +0800224{
225 Elf32_Phdr phdr;
226 int ret;
227 int endian = s->dump_info.d_endian;
228
229 memset(&phdr, 0, sizeof(Elf32_Phdr));
230 phdr.p_type = cpu_convert_to_target32(PT_LOAD, endian);
231 phdr.p_offset = cpu_convert_to_target32(offset, endian);
232 phdr.p_paddr = cpu_convert_to_target32(memory_mapping->phys_addr, endian);
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200233 phdr.p_filesz = cpu_convert_to_target32(filesz, endian);
Wen Congyang783e9b42012-05-07 12:10:47 +0800234 phdr.p_memsz = cpu_convert_to_target32(memory_mapping->length, endian);
235 phdr.p_vaddr = cpu_convert_to_target32(memory_mapping->virt_addr, endian);
236
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200237 assert(memory_mapping->length >= filesz);
238
Wen Congyang783e9b42012-05-07 12:10:47 +0800239 ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
240 if (ret < 0) {
241 dump_error(s, "dump: failed to write program header table.\n");
242 return -1;
243 }
244
245 return 0;
246}
247
248static int write_elf64_note(DumpState *s)
249{
250 Elf64_Phdr phdr;
251 int endian = s->dump_info.d_endian;
Avi Kivitya8170e52012-10-23 12:30:10 +0200252 hwaddr begin = s->memory_offset - s->note_size;
Wen Congyang783e9b42012-05-07 12:10:47 +0800253 int ret;
254
255 memset(&phdr, 0, sizeof(Elf64_Phdr));
256 phdr.p_type = cpu_convert_to_target32(PT_NOTE, endian);
257 phdr.p_offset = cpu_convert_to_target64(begin, endian);
258 phdr.p_paddr = 0;
259 phdr.p_filesz = cpu_convert_to_target64(s->note_size, endian);
260 phdr.p_memsz = cpu_convert_to_target64(s->note_size, endian);
261 phdr.p_vaddr = 0;
262
263 ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
264 if (ret < 0) {
265 dump_error(s, "dump: failed to write program header table.\n");
266 return -1;
267 }
268
269 return 0;
270}
271
Paolo Bonzini0bc3cd62013-04-08 17:29:59 +0200272static inline int cpu_index(CPUState *cpu)
273{
274 return cpu->cpu_index + 1;
275}
276
qiaonuohan6a519912014-02-18 14:11:26 +0800277static int write_elf64_notes(WriteCoreDumpFunction f, DumpState *s)
Wen Congyang783e9b42012-05-07 12:10:47 +0800278{
Andreas Färber0d342822012-12-17 07:12:13 +0100279 CPUState *cpu;
Wen Congyang783e9b42012-05-07 12:10:47 +0800280 int ret;
281 int id;
282
Andreas Färberbdc44642013-06-24 23:50:24 +0200283 CPU_FOREACH(cpu) {
Andreas Färber0d342822012-12-17 07:12:13 +0100284 id = cpu_index(cpu);
qiaonuohan6a519912014-02-18 14:11:26 +0800285 ret = cpu_write_elf64_note(f, cpu, id, s);
Wen Congyang783e9b42012-05-07 12:10:47 +0800286 if (ret < 0) {
287 dump_error(s, "dump: failed to write elf notes.\n");
288 return -1;
289 }
290 }
291
Andreas Färberbdc44642013-06-24 23:50:24 +0200292 CPU_FOREACH(cpu) {
qiaonuohan6a519912014-02-18 14:11:26 +0800293 ret = cpu_write_elf64_qemunote(f, cpu, s);
Wen Congyang783e9b42012-05-07 12:10:47 +0800294 if (ret < 0) {
295 dump_error(s, "dump: failed to write CPU status.\n");
296 return -1;
297 }
298 }
299
300 return 0;
301}
302
303static int write_elf32_note(DumpState *s)
304{
Avi Kivitya8170e52012-10-23 12:30:10 +0200305 hwaddr begin = s->memory_offset - s->note_size;
Wen Congyang783e9b42012-05-07 12:10:47 +0800306 Elf32_Phdr phdr;
307 int endian = s->dump_info.d_endian;
308 int ret;
309
310 memset(&phdr, 0, sizeof(Elf32_Phdr));
311 phdr.p_type = cpu_convert_to_target32(PT_NOTE, endian);
312 phdr.p_offset = cpu_convert_to_target32(begin, endian);
313 phdr.p_paddr = 0;
314 phdr.p_filesz = cpu_convert_to_target32(s->note_size, endian);
315 phdr.p_memsz = cpu_convert_to_target32(s->note_size, endian);
316 phdr.p_vaddr = 0;
317
318 ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
319 if (ret < 0) {
320 dump_error(s, "dump: failed to write program header table.\n");
321 return -1;
322 }
323
324 return 0;
325}
326
qiaonuohan6a519912014-02-18 14:11:26 +0800327static int write_elf32_notes(WriteCoreDumpFunction f, DumpState *s)
Wen Congyang783e9b42012-05-07 12:10:47 +0800328{
Andreas Färber0d342822012-12-17 07:12:13 +0100329 CPUState *cpu;
Wen Congyang783e9b42012-05-07 12:10:47 +0800330 int ret;
331 int id;
332
Andreas Färberbdc44642013-06-24 23:50:24 +0200333 CPU_FOREACH(cpu) {
Andreas Färber0d342822012-12-17 07:12:13 +0100334 id = cpu_index(cpu);
qiaonuohan6a519912014-02-18 14:11:26 +0800335 ret = cpu_write_elf32_note(f, cpu, id, s);
Wen Congyang783e9b42012-05-07 12:10:47 +0800336 if (ret < 0) {
337 dump_error(s, "dump: failed to write elf notes.\n");
338 return -1;
339 }
340 }
341
Andreas Färberbdc44642013-06-24 23:50:24 +0200342 CPU_FOREACH(cpu) {
qiaonuohan6a519912014-02-18 14:11:26 +0800343 ret = cpu_write_elf32_qemunote(f, cpu, s);
Wen Congyang783e9b42012-05-07 12:10:47 +0800344 if (ret < 0) {
345 dump_error(s, "dump: failed to write CPU status.\n");
346 return -1;
347 }
348 }
349
350 return 0;
351}
352
353static int write_elf_section(DumpState *s, int type)
354{
355 Elf32_Shdr shdr32;
356 Elf64_Shdr shdr64;
357 int endian = s->dump_info.d_endian;
358 int shdr_size;
359 void *shdr;
360 int ret;
361
362 if (type == 0) {
363 shdr_size = sizeof(Elf32_Shdr);
364 memset(&shdr32, 0, shdr_size);
365 shdr32.sh_info = cpu_convert_to_target32(s->sh_info, endian);
366 shdr = &shdr32;
367 } else {
368 shdr_size = sizeof(Elf64_Shdr);
369 memset(&shdr64, 0, shdr_size);
370 shdr64.sh_info = cpu_convert_to_target32(s->sh_info, endian);
371 shdr = &shdr64;
372 }
373
374 ret = fd_write_vmcore(&shdr, shdr_size, s);
375 if (ret < 0) {
376 dump_error(s, "dump: failed to write section header table.\n");
377 return -1;
378 }
379
380 return 0;
381}
382
383static int write_data(DumpState *s, void *buf, int length)
384{
385 int ret;
386
387 ret = fd_write_vmcore(buf, length, s);
388 if (ret < 0) {
389 dump_error(s, "dump: failed to save memory.\n");
390 return -1;
391 }
392
393 return 0;
394}
395
396/* write the memroy to vmcore. 1 page per I/O. */
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200397static int write_memory(DumpState *s, GuestPhysBlock *block, ram_addr_t start,
Wen Congyang783e9b42012-05-07 12:10:47 +0800398 int64_t size)
399{
400 int64_t i;
401 int ret;
402
403 for (i = 0; i < size / TARGET_PAGE_SIZE; i++) {
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200404 ret = write_data(s, block->host_addr + start + i * TARGET_PAGE_SIZE,
Wen Congyang783e9b42012-05-07 12:10:47 +0800405 TARGET_PAGE_SIZE);
406 if (ret < 0) {
407 return ret;
408 }
409 }
410
411 if ((size % TARGET_PAGE_SIZE) != 0) {
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200412 ret = write_data(s, block->host_addr + start + i * TARGET_PAGE_SIZE,
Wen Congyang783e9b42012-05-07 12:10:47 +0800413 size % TARGET_PAGE_SIZE);
414 if (ret < 0) {
415 return ret;
416 }
417 }
418
419 return 0;
420}
421
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200422/* get the memory's offset and size in the vmcore */
423static void get_offset_range(hwaddr phys_addr,
424 ram_addr_t mapping_length,
425 DumpState *s,
426 hwaddr *p_offset,
427 hwaddr *p_filesz)
Wen Congyang783e9b42012-05-07 12:10:47 +0800428{
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200429 GuestPhysBlock *block;
Avi Kivitya8170e52012-10-23 12:30:10 +0200430 hwaddr offset = s->memory_offset;
Wen Congyang783e9b42012-05-07 12:10:47 +0800431 int64_t size_in_block, start;
432
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200433 /* When the memory is not stored into vmcore, offset will be -1 */
434 *p_offset = -1;
435 *p_filesz = 0;
436
Wen Congyang783e9b42012-05-07 12:10:47 +0800437 if (s->has_filter) {
438 if (phys_addr < s->begin || phys_addr >= s->begin + s->length) {
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200439 return;
Wen Congyang783e9b42012-05-07 12:10:47 +0800440 }
441 }
442
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200443 QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) {
Wen Congyang783e9b42012-05-07 12:10:47 +0800444 if (s->has_filter) {
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200445 if (block->target_start >= s->begin + s->length ||
446 block->target_end <= s->begin) {
Wen Congyang783e9b42012-05-07 12:10:47 +0800447 /* This block is out of the range */
448 continue;
449 }
450
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200451 if (s->begin <= block->target_start) {
452 start = block->target_start;
Wen Congyang783e9b42012-05-07 12:10:47 +0800453 } else {
454 start = s->begin;
455 }
456
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200457 size_in_block = block->target_end - start;
458 if (s->begin + s->length < block->target_end) {
459 size_in_block -= block->target_end - (s->begin + s->length);
Wen Congyang783e9b42012-05-07 12:10:47 +0800460 }
461 } else {
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200462 start = block->target_start;
463 size_in_block = block->target_end - block->target_start;
Wen Congyang783e9b42012-05-07 12:10:47 +0800464 }
465
466 if (phys_addr >= start && phys_addr < start + size_in_block) {
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200467 *p_offset = phys_addr - start + offset;
468
469 /* The offset range mapped from the vmcore file must not spill over
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200470 * the GuestPhysBlock, clamp it. The rest of the mapping will be
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200471 * zero-filled in memory at load time; see
472 * <http://refspecs.linuxbase.org/elf/gabi4+/ch5.pheader.html>.
473 */
474 *p_filesz = phys_addr + mapping_length <= start + size_in_block ?
475 mapping_length :
476 size_in_block - (phys_addr - start);
477 return;
Wen Congyang783e9b42012-05-07 12:10:47 +0800478 }
479
480 offset += size_in_block;
481 }
Wen Congyang783e9b42012-05-07 12:10:47 +0800482}
483
484static int write_elf_loads(DumpState *s)
485{
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200486 hwaddr offset, filesz;
Wen Congyang783e9b42012-05-07 12:10:47 +0800487 MemoryMapping *memory_mapping;
488 uint32_t phdr_index = 1;
489 int ret;
490 uint32_t max_index;
491
492 if (s->have_section) {
493 max_index = s->sh_info;
494 } else {
495 max_index = s->phdr_num;
496 }
497
498 QTAILQ_FOREACH(memory_mapping, &s->list.head, next) {
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200499 get_offset_range(memory_mapping->phys_addr,
500 memory_mapping->length,
501 s, &offset, &filesz);
Wen Congyang783e9b42012-05-07 12:10:47 +0800502 if (s->dump_info.d_class == ELFCLASS64) {
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200503 ret = write_elf64_load(s, memory_mapping, phdr_index++, offset,
504 filesz);
Wen Congyang783e9b42012-05-07 12:10:47 +0800505 } else {
Laszlo Ersek2cac2602013-08-06 12:37:08 +0200506 ret = write_elf32_load(s, memory_mapping, phdr_index++, offset,
507 filesz);
Wen Congyang783e9b42012-05-07 12:10:47 +0800508 }
509
510 if (ret < 0) {
511 return -1;
512 }
513
514 if (phdr_index >= max_index) {
515 break;
516 }
517 }
518
519 return 0;
520}
521
522/* write elf header, PT_NOTE and elf note to vmcore. */
523static int dump_begin(DumpState *s)
524{
525 int ret;
526
527 /*
528 * the vmcore's format is:
529 * --------------
530 * | elf header |
531 * --------------
532 * | PT_NOTE |
533 * --------------
534 * | PT_LOAD |
535 * --------------
536 * | ...... |
537 * --------------
538 * | PT_LOAD |
539 * --------------
540 * | sec_hdr |
541 * --------------
542 * | elf note |
543 * --------------
544 * | memory |
545 * --------------
546 *
547 * we only know where the memory is saved after we write elf note into
548 * vmcore.
549 */
550
551 /* write elf header to vmcore */
552 if (s->dump_info.d_class == ELFCLASS64) {
553 ret = write_elf64_header(s);
554 } else {
555 ret = write_elf32_header(s);
556 }
557 if (ret < 0) {
558 return -1;
559 }
560
561 if (s->dump_info.d_class == ELFCLASS64) {
562 /* write PT_NOTE to vmcore */
563 if (write_elf64_note(s) < 0) {
564 return -1;
565 }
566
567 /* write all PT_LOAD to vmcore */
568 if (write_elf_loads(s) < 0) {
569 return -1;
570 }
571
572 /* write section to vmcore */
573 if (s->have_section) {
574 if (write_elf_section(s, 1) < 0) {
575 return -1;
576 }
577 }
578
579 /* write notes to vmcore */
qiaonuohan6a519912014-02-18 14:11:26 +0800580 if (write_elf64_notes(fd_write_vmcore, s) < 0) {
Wen Congyang783e9b42012-05-07 12:10:47 +0800581 return -1;
582 }
583
584 } else {
585 /* write PT_NOTE to vmcore */
586 if (write_elf32_note(s) < 0) {
587 return -1;
588 }
589
590 /* write all PT_LOAD to vmcore */
591 if (write_elf_loads(s) < 0) {
592 return -1;
593 }
594
595 /* write section to vmcore */
596 if (s->have_section) {
597 if (write_elf_section(s, 0) < 0) {
598 return -1;
599 }
600 }
601
602 /* write notes to vmcore */
qiaonuohan6a519912014-02-18 14:11:26 +0800603 if (write_elf32_notes(fd_write_vmcore, s) < 0) {
Wen Congyang783e9b42012-05-07 12:10:47 +0800604 return -1;
605 }
606 }
607
608 return 0;
609}
610
611/* write PT_LOAD to vmcore */
612static int dump_completed(DumpState *s)
613{
614 dump_cleanup(s);
615 return 0;
616}
617
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200618static int get_next_block(DumpState *s, GuestPhysBlock *block)
Wen Congyang783e9b42012-05-07 12:10:47 +0800619{
620 while (1) {
Paolo Bonzinia3161032012-11-14 15:54:48 +0100621 block = QTAILQ_NEXT(block, next);
Wen Congyang783e9b42012-05-07 12:10:47 +0800622 if (!block) {
623 /* no more block */
624 return 1;
625 }
626
627 s->start = 0;
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200628 s->next_block = block;
Wen Congyang783e9b42012-05-07 12:10:47 +0800629 if (s->has_filter) {
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200630 if (block->target_start >= s->begin + s->length ||
631 block->target_end <= s->begin) {
Wen Congyang783e9b42012-05-07 12:10:47 +0800632 /* This block is out of the range */
633 continue;
634 }
635
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200636 if (s->begin > block->target_start) {
637 s->start = s->begin - block->target_start;
Wen Congyang783e9b42012-05-07 12:10:47 +0800638 }
639 }
640
641 return 0;
642 }
643}
644
645/* write all memory to vmcore */
646static int dump_iterate(DumpState *s)
647{
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200648 GuestPhysBlock *block;
Wen Congyang783e9b42012-05-07 12:10:47 +0800649 int64_t size;
650 int ret;
651
652 while (1) {
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200653 block = s->next_block;
Wen Congyang783e9b42012-05-07 12:10:47 +0800654
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200655 size = block->target_end - block->target_start;
Wen Congyang783e9b42012-05-07 12:10:47 +0800656 if (s->has_filter) {
657 size -= s->start;
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200658 if (s->begin + s->length < block->target_end) {
659 size -= block->target_end - (s->begin + s->length);
Wen Congyang783e9b42012-05-07 12:10:47 +0800660 }
661 }
662 ret = write_memory(s, block, s->start, size);
663 if (ret == -1) {
664 return ret;
665 }
666
667 ret = get_next_block(s, block);
668 if (ret == 1) {
669 dump_completed(s);
670 return 0;
671 }
672 }
673}
674
675static int create_vmcore(DumpState *s)
676{
677 int ret;
678
679 ret = dump_begin(s);
680 if (ret < 0) {
681 return -1;
682 }
683
684 ret = dump_iterate(s);
685 if (ret < 0) {
686 return -1;
687 }
688
689 return 0;
690}
691
qiaonuohanfda05382014-02-18 14:11:27 +0800692static int write_start_flat_header(int fd)
693{
694 uint8_t *buf;
695 MakedumpfileHeader mh;
696 int ret = 0;
697
698 memset(&mh, 0, sizeof(mh));
699 strncpy(mh.signature, MAKEDUMPFILE_SIGNATURE,
700 strlen(MAKEDUMPFILE_SIGNATURE));
701
702 mh.type = cpu_to_be64(TYPE_FLAT_HEADER);
703 mh.version = cpu_to_be64(VERSION_FLAT_HEADER);
704
705 buf = g_malloc0(MAX_SIZE_MDF_HEADER);
706 memcpy(buf, &mh, sizeof(mh));
707
708 size_t written_size;
709 written_size = qemu_write_full(fd, buf, MAX_SIZE_MDF_HEADER);
710 if (written_size != MAX_SIZE_MDF_HEADER) {
711 ret = -1;
712 }
713
714 g_free(buf);
715 return ret;
716}
717
718static int write_end_flat_header(int fd)
719{
720 MakedumpfileDataHeader mdh;
721
722 mdh.offset = END_FLAG_FLAT_HEADER;
723 mdh.buf_size = END_FLAG_FLAT_HEADER;
724
725 size_t written_size;
726 written_size = qemu_write_full(fd, &mdh, sizeof(mdh));
727 if (written_size != sizeof(mdh)) {
728 return -1;
729 }
730
731 return 0;
732}
733
qiaonuohan5d31bab2014-02-18 14:11:28 +0800734static int write_buffer(int fd, off_t offset, const void *buf, size_t size)
735{
736 size_t written_size;
737 MakedumpfileDataHeader mdh;
738
739 mdh.offset = cpu_to_be64(offset);
740 mdh.buf_size = cpu_to_be64(size);
741
742 written_size = qemu_write_full(fd, &mdh, sizeof(mdh));
743 if (written_size != sizeof(mdh)) {
744 return -1;
745 }
746
747 written_size = qemu_write_full(fd, buf, size);
748 if (written_size != size) {
749 return -1;
750 }
751
752 return 0;
753}
754
qiaonuohan4835ef72014-02-18 14:11:29 +0800755static int buf_write_note(const void *buf, size_t size, void *opaque)
756{
757 DumpState *s = opaque;
758
759 /* note_buf is not enough */
760 if (s->note_buf_offset + size > s->note_size) {
761 return -1;
762 }
763
764 memcpy(s->note_buf + s->note_buf_offset, buf, size);
765
766 s->note_buf_offset += size;
767
768 return 0;
769}
770
Wen Congyang783e9b42012-05-07 12:10:47 +0800771static ram_addr_t get_start_block(DumpState *s)
772{
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200773 GuestPhysBlock *block;
Wen Congyang783e9b42012-05-07 12:10:47 +0800774
775 if (!s->has_filter) {
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200776 s->next_block = QTAILQ_FIRST(&s->guest_phys_blocks.head);
Wen Congyang783e9b42012-05-07 12:10:47 +0800777 return 0;
778 }
779
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200780 QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) {
781 if (block->target_start >= s->begin + s->length ||
782 block->target_end <= s->begin) {
Wen Congyang783e9b42012-05-07 12:10:47 +0800783 /* This block is out of the range */
784 continue;
785 }
786
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200787 s->next_block = block;
788 if (s->begin > block->target_start) {
789 s->start = s->begin - block->target_start;
Wen Congyang783e9b42012-05-07 12:10:47 +0800790 } else {
791 s->start = 0;
792 }
793 return s->start;
794 }
795
796 return -1;
797}
798
799static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
800 int64_t begin, int64_t length, Error **errp)
801{
Andreas Färber182735e2013-05-29 22:29:20 +0200802 CPUState *cpu;
Wen Congyang783e9b42012-05-07 12:10:47 +0800803 int nr_cpus;
Andreas Färber11ed09c2013-05-29 21:54:03 +0200804 Error *err = NULL;
Wen Congyang783e9b42012-05-07 12:10:47 +0800805 int ret;
806
807 if (runstate_is_running()) {
808 vm_stop(RUN_STATE_SAVE_VM);
809 s->resume = true;
810 } else {
811 s->resume = false;
812 }
813
Laszlo Ersek5ee163e2013-08-06 12:37:09 +0200814 /* If we use KVM, we should synchronize the registers before we get dump
815 * info or physmap info.
Wen Congyang783e9b42012-05-07 12:10:47 +0800816 */
Andreas Färber1b3509c2013-06-09 16:48:29 +0200817 cpu_synchronize_all_states();
Wen Congyang783e9b42012-05-07 12:10:47 +0800818 nr_cpus = 0;
Andreas Färberbdc44642013-06-24 23:50:24 +0200819 CPU_FOREACH(cpu) {
Wen Congyang783e9b42012-05-07 12:10:47 +0800820 nr_cpus++;
821 }
822
Laszlo Ersek5ee163e2013-08-06 12:37:09 +0200823 s->errp = errp;
824 s->fd = fd;
825 s->has_filter = has_filter;
826 s->begin = begin;
827 s->length = length;
828
829 guest_phys_blocks_init(&s->guest_phys_blocks);
Laszlo Ersekc5d7f602013-08-06 12:37:10 +0200830 guest_phys_blocks_append(&s->guest_phys_blocks);
Laszlo Ersek5ee163e2013-08-06 12:37:09 +0200831
832 s->start = get_start_block(s);
833 if (s->start == -1) {
834 error_set(errp, QERR_INVALID_PARAMETER, "begin");
835 goto cleanup;
836 }
837
838 /* get dump info: endian, class and architecture.
839 * If the target architecture is not supported, cpu_get_dump_info() will
840 * return -1.
841 */
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200842 ret = cpu_get_dump_info(&s->dump_info, &s->guest_phys_blocks);
Wen Congyang783e9b42012-05-07 12:10:47 +0800843 if (ret < 0) {
844 error_set(errp, QERR_UNSUPPORTED);
845 goto cleanup;
846 }
847
Paolo Bonzini4720bd02012-06-07 08:48:09 +0200848 s->note_size = cpu_get_note_size(s->dump_info.d_class,
849 s->dump_info.d_machine, nr_cpus);
Aneesh Kumar K.Vbb6b6842013-10-01 21:49:32 +0530850 if (s->note_size < 0) {
Paolo Bonzini4720bd02012-06-07 08:48:09 +0200851 error_set(errp, QERR_UNSUPPORTED);
852 goto cleanup;
853 }
854
Wen Congyang783e9b42012-05-07 12:10:47 +0800855 /* get memory mapping */
856 memory_mapping_list_init(&s->list);
857 if (paging) {
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200858 qemu_get_guest_memory_mapping(&s->list, &s->guest_phys_blocks, &err);
Andreas Färber11ed09c2013-05-29 21:54:03 +0200859 if (err != NULL) {
860 error_propagate(errp, err);
861 goto cleanup;
862 }
Wen Congyang783e9b42012-05-07 12:10:47 +0800863 } else {
Laszlo Ersek56c4bfb2013-08-06 12:37:11 +0200864 qemu_get_guest_simple_memory_mapping(&s->list, &s->guest_phys_blocks);
Wen Congyang783e9b42012-05-07 12:10:47 +0800865 }
866
867 if (s->has_filter) {
868 memory_mapping_filter(&s->list, s->begin, s->length);
869 }
870
871 /*
872 * calculate phdr_num
873 *
874 * the type of ehdr->e_phnum is uint16_t, so we should avoid overflow
875 */
876 s->phdr_num = 1; /* PT_NOTE */
877 if (s->list.num < UINT16_MAX - 2) {
878 s->phdr_num += s->list.num;
879 s->have_section = false;
880 } else {
881 s->have_section = true;
882 s->phdr_num = PN_XNUM;
883 s->sh_info = 1; /* PT_NOTE */
884
885 /* the type of shdr->sh_info is uint32_t, so we should avoid overflow */
886 if (s->list.num <= UINT32_MAX - 1) {
887 s->sh_info += s->list.num;
888 } else {
889 s->sh_info = UINT32_MAX;
890 }
891 }
892
Wen Congyang783e9b42012-05-07 12:10:47 +0800893 if (s->dump_info.d_class == ELFCLASS64) {
894 if (s->have_section) {
895 s->memory_offset = sizeof(Elf64_Ehdr) +
896 sizeof(Elf64_Phdr) * s->sh_info +
897 sizeof(Elf64_Shdr) + s->note_size;
898 } else {
899 s->memory_offset = sizeof(Elf64_Ehdr) +
900 sizeof(Elf64_Phdr) * s->phdr_num + s->note_size;
901 }
902 } else {
903 if (s->have_section) {
904 s->memory_offset = sizeof(Elf32_Ehdr) +
905 sizeof(Elf32_Phdr) * s->sh_info +
906 sizeof(Elf32_Shdr) + s->note_size;
907 } else {
908 s->memory_offset = sizeof(Elf32_Ehdr) +
909 sizeof(Elf32_Phdr) * s->phdr_num + s->note_size;
910 }
911 }
912
913 return 0;
914
915cleanup:
Laszlo Ersek5ee163e2013-08-06 12:37:09 +0200916 guest_phys_blocks_free(&s->guest_phys_blocks);
917
Wen Congyang783e9b42012-05-07 12:10:47 +0800918 if (s->resume) {
919 vm_start();
920 }
921
922 return -1;
923}
924
925void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
926 int64_t begin, bool has_length, int64_t length,
927 Error **errp)
928{
929 const char *p;
930 int fd = -1;
931 DumpState *s;
932 int ret;
933
934 if (has_begin && !has_length) {
935 error_set(errp, QERR_MISSING_PARAMETER, "length");
936 return;
937 }
938 if (!has_begin && has_length) {
939 error_set(errp, QERR_MISSING_PARAMETER, "begin");
940 return;
941 }
942
943#if !defined(WIN32)
944 if (strstart(file, "fd:", &p)) {
Paolo Bonzinia9940fc2012-09-20 16:50:32 +0200945 fd = monitor_get_fd(cur_mon, p, errp);
Wen Congyang783e9b42012-05-07 12:10:47 +0800946 if (fd == -1) {
Wen Congyang783e9b42012-05-07 12:10:47 +0800947 return;
948 }
949 }
950#endif
951
952 if (strstart(file, "file:", &p)) {
953 fd = qemu_open(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR);
954 if (fd < 0) {
Luiz Capitulino75817662013-06-07 14:36:01 -0400955 error_setg_file_open(errp, errno, p);
Wen Congyang783e9b42012-05-07 12:10:47 +0800956 return;
957 }
958 }
959
960 if (fd == -1) {
961 error_set(errp, QERR_INVALID_PARAMETER, "protocol");
962 return;
963 }
964
Laszlo Ersek5ee163e2013-08-06 12:37:09 +0200965 s = g_malloc0(sizeof(DumpState));
Wen Congyang783e9b42012-05-07 12:10:47 +0800966
967 ret = dump_init(s, fd, paging, has_begin, begin, length, errp);
968 if (ret < 0) {
969 g_free(s);
970 return;
971 }
972
973 if (create_vmcore(s) < 0 && !error_is_set(s->errp)) {
974 error_set(errp, QERR_IO_ERROR);
975 }
976
977 g_free(s);
978}