+ } while (!get_next_block(s, block));
+
+ dump_completed(s);
+}
+
+static void create_vmcore(DumpState *s, Error **errp)
+{
+ Error *local_err = NULL;
+
+ dump_begin(s, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ dump_iterate(s, errp);
+}
+
+static int write_start_flat_header(int fd)
+{
+ MakedumpfileHeader *mh;
+ int ret = 0;
+
+ QEMU_BUILD_BUG_ON(sizeof *mh > MAX_SIZE_MDF_HEADER);
+ mh = g_malloc0(MAX_SIZE_MDF_HEADER);
+
+ memcpy(mh->signature, MAKEDUMPFILE_SIGNATURE,
+ MIN(sizeof mh->signature, sizeof MAKEDUMPFILE_SIGNATURE));
+
+ mh->type = cpu_to_be64(TYPE_FLAT_HEADER);
+ mh->version = cpu_to_be64(VERSION_FLAT_HEADER);
+
+ size_t written_size;
+ written_size = qemu_write_full(fd, mh, MAX_SIZE_MDF_HEADER);
+ if (written_size != MAX_SIZE_MDF_HEADER) {
+ ret = -1;
+ }
+
+ g_free(mh);
+ return ret;
+}
+
+static int write_end_flat_header(int fd)
+{
+ MakedumpfileDataHeader mdh;
+
+ mdh.offset = END_FLAG_FLAT_HEADER;
+ mdh.buf_size = END_FLAG_FLAT_HEADER;
+
+ size_t written_size;
+ written_size = qemu_write_full(fd, &mdh, sizeof(mdh));
+ if (written_size != sizeof(mdh)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int write_buffer(int fd, off_t offset, const void *buf, size_t size)
+{
+ size_t written_size;
+ MakedumpfileDataHeader mdh;
+
+ mdh.offset = cpu_to_be64(offset);
+ mdh.buf_size = cpu_to_be64(size);
+
+ written_size = qemu_write_full(fd, &mdh, sizeof(mdh));
+ if (written_size != sizeof(mdh)) {
+ return -1;
+ }
+
+ written_size = qemu_write_full(fd, buf, size);
+ if (written_size != size) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int buf_write_note(const void *buf, size_t size, void *opaque)
+{
+ DumpState *s = opaque;
+
+ /* note_buf is not enough */
+ if (s->note_buf_offset + size > s->note_size) {
+ return -1;
+ }
+
+ memcpy(s->note_buf + s->note_buf_offset, buf, size);
+
+ s->note_buf_offset += size;
+
+ return 0;
+}
+
+/* write common header, sub header and elf note to vmcore */
+static void create_header32(DumpState *s, Error **errp)
+{
+ DiskDumpHeader32 *dh = NULL;
+ KdumpSubHeader32 *kh = NULL;
+ size_t size;
+ uint32_t block_size;
+ uint32_t sub_hdr_size;
+ uint32_t bitmap_blocks;
+ uint32_t status = 0;
+ uint64_t offset_note;
+ Error *local_err = NULL;
+
+ /* write common header, the version of kdump-compressed format is 6th */
+ size = sizeof(DiskDumpHeader32);
+ dh = g_malloc0(size);
+
+ strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE));
+ dh->header_version = cpu_to_dump32(s, 6);
+ block_size = TARGET_PAGE_SIZE;
+ dh->block_size = cpu_to_dump32(s, block_size);
+ sub_hdr_size = sizeof(struct KdumpSubHeader32) + s->note_size;
+ sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size);
+ dh->sub_hdr_size = cpu_to_dump32(s, sub_hdr_size);
+ /* dh->max_mapnr may be truncated, full 64bit is in kh.max_mapnr_64 */
+ dh->max_mapnr = cpu_to_dump32(s, MIN(s->max_mapnr, UINT_MAX));
+ dh->nr_cpus = cpu_to_dump32(s, s->nr_cpus);
+ bitmap_blocks = DIV_ROUND_UP(s->len_dump_bitmap, block_size) * 2;
+ dh->bitmap_blocks = cpu_to_dump32(s, bitmap_blocks);
+ strncpy(dh->utsname.machine, ELF_MACHINE_UNAME, sizeof(dh->utsname.machine));
+
+ if (s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) {
+ status |= DUMP_DH_COMPRESSED_ZLIB;
+ }
+#ifdef CONFIG_LZO
+ if (s->flag_compress & DUMP_DH_COMPRESSED_LZO) {
+ status |= DUMP_DH_COMPRESSED_LZO;
+ }
+#endif
+#ifdef CONFIG_SNAPPY
+ if (s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) {
+ status |= DUMP_DH_COMPRESSED_SNAPPY;
+ }
+#endif
+ dh->status = cpu_to_dump32(s, status);
+
+ if (write_buffer(s->fd, 0, dh, size) < 0) {
+ dump_error(s, "dump: failed to write disk dump header", errp);
+ goto out;
+ }
+
+ /* write sub header */
+ size = sizeof(KdumpSubHeader32);
+ kh = g_malloc0(size);
+
+ /* 64bit max_mapnr_64 */
+ kh->max_mapnr_64 = cpu_to_dump64(s, s->max_mapnr);
+ kh->phys_base = cpu_to_dump32(s, PHYS_BASE);
+ kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL);
+
+ offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size;
+ kh->offset_note = cpu_to_dump64(s, offset_note);
+ kh->note_size = cpu_to_dump32(s, s->note_size);
+
+ if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
+ block_size, kh, size) < 0) {
+ dump_error(s, "dump: failed to write kdump sub header", errp);
+ goto out;
+ }
+
+ /* write note */
+ s->note_buf = g_malloc0(s->note_size);
+ s->note_buf_offset = 0;
+
+ /* use s->note_buf to store notes temporarily */
+ write_elf32_notes(buf_write_note, s, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
+ if (write_buffer(s->fd, offset_note, s->note_buf,
+ s->note_size) < 0) {
+ dump_error(s, "dump: failed to write notes", errp);
+ goto out;
+ }
+
+ /* get offset of dump_bitmap */
+ s->offset_dump_bitmap = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size) *
+ block_size;
+
+ /* get offset of page */
+ s->offset_page = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size + bitmap_blocks) *
+ block_size;
+
+out:
+ g_free(dh);
+ g_free(kh);
+ g_free(s->note_buf);
+}
+
+/* write common header, sub header and elf note to vmcore */
+static void create_header64(DumpState *s, Error **errp)
+{
+ DiskDumpHeader64 *dh = NULL;
+ KdumpSubHeader64 *kh = NULL;
+ size_t size;
+ uint32_t block_size;
+ uint32_t sub_hdr_size;
+ uint32_t bitmap_blocks;
+ uint32_t status = 0;
+ uint64_t offset_note;
+ Error *local_err = NULL;
+
+ /* write common header, the version of kdump-compressed format is 6th */
+ size = sizeof(DiskDumpHeader64);
+ dh = g_malloc0(size);
+
+ strncpy(dh->signature, KDUMP_SIGNATURE, strlen(KDUMP_SIGNATURE));
+ dh->header_version = cpu_to_dump32(s, 6);
+ block_size = TARGET_PAGE_SIZE;
+ dh->block_size = cpu_to_dump32(s, block_size);
+ sub_hdr_size = sizeof(struct KdumpSubHeader64) + s->note_size;
+ sub_hdr_size = DIV_ROUND_UP(sub_hdr_size, block_size);
+ dh->sub_hdr_size = cpu_to_dump32(s, sub_hdr_size);
+ /* dh->max_mapnr may be truncated, full 64bit is in kh.max_mapnr_64 */
+ dh->max_mapnr = cpu_to_dump32(s, MIN(s->max_mapnr, UINT_MAX));
+ dh->nr_cpus = cpu_to_dump32(s, s->nr_cpus);
+ bitmap_blocks = DIV_ROUND_UP(s->len_dump_bitmap, block_size) * 2;
+ dh->bitmap_blocks = cpu_to_dump32(s, bitmap_blocks);
+ strncpy(dh->utsname.machine, ELF_MACHINE_UNAME, sizeof(dh->utsname.machine));
+
+ if (s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) {
+ status |= DUMP_DH_COMPRESSED_ZLIB;
+ }
+#ifdef CONFIG_LZO
+ if (s->flag_compress & DUMP_DH_COMPRESSED_LZO) {
+ status |= DUMP_DH_COMPRESSED_LZO;
+ }
+#endif
+#ifdef CONFIG_SNAPPY
+ if (s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) {
+ status |= DUMP_DH_COMPRESSED_SNAPPY;
+ }
+#endif
+ dh->status = cpu_to_dump32(s, status);
+
+ if (write_buffer(s->fd, 0, dh, size) < 0) {
+ dump_error(s, "dump: failed to write disk dump header", errp);
+ goto out;
+ }
+
+ /* write sub header */
+ size = sizeof(KdumpSubHeader64);
+ kh = g_malloc0(size);
+
+ /* 64bit max_mapnr_64 */
+ kh->max_mapnr_64 = cpu_to_dump64(s, s->max_mapnr);
+ kh->phys_base = cpu_to_dump64(s, PHYS_BASE);
+ kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL);
+
+ offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size;
+ kh->offset_note = cpu_to_dump64(s, offset_note);
+ kh->note_size = cpu_to_dump64(s, s->note_size);
+
+ if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
+ block_size, kh, size) < 0) {
+ dump_error(s, "dump: failed to write kdump sub header", errp);
+ goto out;
+ }
+
+ /* write note */
+ s->note_buf = g_malloc0(s->note_size);
+ s->note_buf_offset = 0;
+
+ /* use s->note_buf to store notes temporarily */
+ write_elf64_notes(buf_write_note, s, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto out;
+ }
+
+ if (write_buffer(s->fd, offset_note, s->note_buf,
+ s->note_size) < 0) {
+ dump_error(s, "dump: failed to write notes", errp);
+ goto out;
+ }
+
+ /* get offset of dump_bitmap */
+ s->offset_dump_bitmap = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size) *
+ block_size;
+
+ /* get offset of page */
+ s->offset_page = (DISKDUMP_HEADER_BLOCKS + sub_hdr_size + bitmap_blocks) *
+ block_size;
+
+out:
+ g_free(dh);
+ g_free(kh);
+ g_free(s->note_buf);
+}
+
+static void write_dump_header(DumpState *s, Error **errp)
+{
+ Error *local_err = NULL;
+
+ if (s->dump_info.d_class == ELFCLASS32) {
+ create_header32(s, &local_err);
+ } else {
+ create_header64(s, &local_err);
+ }
+ if (local_err) {
+ error_propagate(errp, local_err);
+ }
+}
+
+/*
+ * set dump_bitmap sequencely. the bit before last_pfn is not allowed to be
+ * rewritten, so if need to set the first bit, set last_pfn and pfn to 0.
+ * set_dump_bitmap will always leave the recently set bit un-sync. And setting
+ * (last bit + sizeof(buf) * 8) to 0 will do flushing the content in buf into
+ * vmcore, ie. synchronizing un-sync bit into vmcore.
+ */
+static int set_dump_bitmap(uint64_t last_pfn, uint64_t pfn, bool value,
+ uint8_t *buf, DumpState *s)
+{
+ off_t old_offset, new_offset;
+ off_t offset_bitmap1, offset_bitmap2;
+ uint32_t byte, bit;
+
+ /* should not set the previous place */
+ assert(last_pfn <= pfn);
+
+ /*
+ * if the bit needed to be set is not cached in buf, flush the data in buf
+ * to vmcore firstly.
+ * making new_offset be bigger than old_offset can also sync remained data
+ * into vmcore.
+ */
+ old_offset = BUFSIZE_BITMAP * (last_pfn / PFN_BUFBITMAP);
+ new_offset = BUFSIZE_BITMAP * (pfn / PFN_BUFBITMAP);
+
+ while (old_offset < new_offset) {
+ /* calculate the offset and write dump_bitmap */
+ offset_bitmap1 = s->offset_dump_bitmap + old_offset;
+ if (write_buffer(s->fd, offset_bitmap1, buf,
+ BUFSIZE_BITMAP) < 0) {
+ return -1;
+ }
+
+ /* dump level 1 is chosen, so 1st and 2nd bitmap are same */
+ offset_bitmap2 = s->offset_dump_bitmap + s->len_dump_bitmap +
+ old_offset;
+ if (write_buffer(s->fd, offset_bitmap2, buf,
+ BUFSIZE_BITMAP) < 0) {
+ return -1;
+ }
+
+ memset(buf, 0, BUFSIZE_BITMAP);
+ old_offset += BUFSIZE_BITMAP;
+ }
+
+ /* get the exact place of the bit in the buf, and set it */
+ byte = (pfn % PFN_BUFBITMAP) / CHAR_BIT;
+ bit = (pfn % PFN_BUFBITMAP) % CHAR_BIT;
+ if (value) {
+ buf[byte] |= 1u << bit;
+ } else {
+ buf[byte] &= ~(1u << bit);
+ }
+
+ return 0;
+}
+
+/*
+ * exam every page and return the page frame number and the address of the page.
+ * bufptr can be NULL. note: the blocks here is supposed to reflect guest-phys
+ * blocks, so block->target_start and block->target_end should be interal
+ * multiples of the target page size.
+ */
+static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr,
+ uint8_t **bufptr, DumpState *s)
+{
+ GuestPhysBlock *block = *blockptr;
+ hwaddr addr;
+ uint8_t *buf;
+
+ /* block == NULL means the start of the iteration */
+ if (!block) {
+ block = QTAILQ_FIRST(&s->guest_phys_blocks.head);
+ *blockptr = block;
+ assert((block->target_start & ~TARGET_PAGE_MASK) == 0);
+ assert((block->target_end & ~TARGET_PAGE_MASK) == 0);
+ *pfnptr = paddr_to_pfn(block->target_start);
+ if (bufptr) {
+ *bufptr = block->host_addr;
+ }
+ return true;
+ }
+
+ *pfnptr = *pfnptr + 1;
+ addr = pfn_to_paddr(*pfnptr);
+
+ if ((addr >= block->target_start) &&
+ (addr + TARGET_PAGE_SIZE <= block->target_end)) {
+ buf = block->host_addr + (addr - block->target_start);
+ } else {
+ /* the next page is in the next block */
+ block = QTAILQ_NEXT(block, next);
+ *blockptr = block;
+ if (!block) {
+ return false;
+ }
+ assert((block->target_start & ~TARGET_PAGE_MASK) == 0);
+ assert((block->target_end & ~TARGET_PAGE_MASK) == 0);
+ *pfnptr = paddr_to_pfn(block->target_start);
+ buf = block->host_addr;
+ }
+
+ if (bufptr) {
+ *bufptr = buf;
+ }
+
+ return true;
+}
+
+static void write_dump_bitmap(DumpState *s, Error **errp)
+{
+ int ret = 0;
+ uint64_t last_pfn, pfn;
+ void *dump_bitmap_buf;
+ size_t num_dumpable;
+ GuestPhysBlock *block_iter = NULL;
+
+ /* dump_bitmap_buf is used to store dump_bitmap temporarily */
+ dump_bitmap_buf = g_malloc0(BUFSIZE_BITMAP);
+
+ num_dumpable = 0;
+ last_pfn = 0;
+
+ /*
+ * exam memory page by page, and set the bit in dump_bitmap corresponded
+ * to the existing page.
+ */
+ while (get_next_page(&block_iter, &pfn, NULL, s)) {
+ ret = set_dump_bitmap(last_pfn, pfn, true, dump_bitmap_buf, s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to set dump_bitmap", errp);
+ goto out;
+ }
+
+ last_pfn = pfn;
+ num_dumpable++;
+ }
+
+ /*
+ * set_dump_bitmap will always leave the recently set bit un-sync. Here we
+ * set last_pfn + PFN_BUFBITMAP to 0 and those set but un-sync bit will be
+ * synchronized into vmcore.
+ */
+ if (num_dumpable > 0) {
+ ret = set_dump_bitmap(last_pfn, last_pfn + PFN_BUFBITMAP, false,
+ dump_bitmap_buf, s);
+ if (ret < 0) {
+ dump_error(s, "dump: failed to sync dump_bitmap", errp);
+ goto out;
+ }
+ }
+
+ /* number of dumpable pages that will be dumped later */
+ s->num_dumpable = num_dumpable;
+
+out:
+ g_free(dump_bitmap_buf);
+}
+
+static void prepare_data_cache(DataCache *data_cache, DumpState *s,
+ off_t offset)
+{
+ data_cache->fd = s->fd;
+ data_cache->data_size = 0;
+ data_cache->buf_size = BUFSIZE_DATA_CACHE;
+ data_cache->buf = g_malloc0(BUFSIZE_DATA_CACHE);
+ data_cache->offset = offset;
+}
+
+static int write_cache(DataCache *dc, const void *buf, size_t size,
+ bool flag_sync)
+{
+ /*
+ * dc->buf_size should not be less than size, otherwise dc will never be
+ * enough
+ */
+ assert(size <= dc->buf_size);
+
+ /*
+ * if flag_sync is set, synchronize data in dc->buf into vmcore.
+ * otherwise check if the space is enough for caching data in buf, if not,
+ * write the data in dc->buf to dc->fd and reset dc->buf
+ */
+ if ((!flag_sync && dc->data_size + size > dc->buf_size) ||
+ (flag_sync && dc->data_size > 0)) {
+ if (write_buffer(dc->fd, dc->offset, dc->buf, dc->data_size) < 0) {
+ return -1;