xref: /qemu/dump/win_dump.c (revision 92d1b3d5086c182bab66fd1814c4a04ba1e59337)
12da91b54SViktor Prutyanov /*
22da91b54SViktor Prutyanov  * Windows crashdump
32da91b54SViktor Prutyanov  *
42da91b54SViktor Prutyanov  * Copyright (c) 2018 Virtuozzo International GmbH
52da91b54SViktor Prutyanov  *
62da91b54SViktor Prutyanov  * This work is licensed under the terms of the GNU GPL, version 2 or later.
72da91b54SViktor Prutyanov  * See the COPYING file in the top-level directory.
82da91b54SViktor Prutyanov  *
92da91b54SViktor Prutyanov  */
102da91b54SViktor Prutyanov 
112da91b54SViktor Prutyanov #include "qemu/osdep.h"
122da91b54SViktor Prutyanov #include "qemu/cutils.h"
132da91b54SViktor Prutyanov #include "elf.h"
142da91b54SViktor Prutyanov #include "cpu.h"
152da91b54SViktor Prutyanov #include "exec/hwaddr.h"
162da91b54SViktor Prutyanov #include "monitor/monitor.h"
172da91b54SViktor Prutyanov #include "sysemu/kvm.h"
182da91b54SViktor Prutyanov #include "sysemu/dump.h"
192da91b54SViktor Prutyanov #include "sysemu/sysemu.h"
202da91b54SViktor Prutyanov #include "sysemu/memory_mapping.h"
212da91b54SViktor Prutyanov #include "sysemu/cpus.h"
222da91b54SViktor Prutyanov #include "qapi/error.h"
232da91b54SViktor Prutyanov #include "qapi/qmp/qerror.h"
242da91b54SViktor Prutyanov #include "qemu/error-report.h"
252da91b54SViktor Prutyanov #include "hw/misc/vmcoreinfo.h"
262da91b54SViktor Prutyanov #include "win_dump.h"
272da91b54SViktor Prutyanov 
282da91b54SViktor Prutyanov static size_t write_run(WinDumpPhyMemRun64 *run, int fd, Error **errp)
292da91b54SViktor Prutyanov {
302da91b54SViktor Prutyanov     void *buf;
312da91b54SViktor Prutyanov     uint64_t addr = run->BasePage << TARGET_PAGE_BITS;
322da91b54SViktor Prutyanov     uint64_t size = run->PageCount << TARGET_PAGE_BITS;
332da91b54SViktor Prutyanov     uint64_t len = size;
342da91b54SViktor Prutyanov 
352da91b54SViktor Prutyanov     buf = cpu_physical_memory_map(addr, &len, false);
362da91b54SViktor Prutyanov     if (!buf) {
372da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to map run");
382da91b54SViktor Prutyanov         return 0;
392da91b54SViktor Prutyanov     }
402da91b54SViktor Prutyanov     if (len != size) {
412da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to map entire run");
422da91b54SViktor Prutyanov         len = 0;
432da91b54SViktor Prutyanov         goto out_unmap;
442da91b54SViktor Prutyanov     }
452da91b54SViktor Prutyanov 
462da91b54SViktor Prutyanov     len = qemu_write_full(fd, buf, len);
472da91b54SViktor Prutyanov     if (len != size) {
482da91b54SViktor Prutyanov         error_setg(errp, QERR_IO_ERROR);
492da91b54SViktor Prutyanov     }
502da91b54SViktor Prutyanov 
512da91b54SViktor Prutyanov out_unmap:
522da91b54SViktor Prutyanov     cpu_physical_memory_unmap(buf, addr, false, len);
532da91b54SViktor Prutyanov 
542da91b54SViktor Prutyanov     return len;
552da91b54SViktor Prutyanov }
562da91b54SViktor Prutyanov 
572da91b54SViktor Prutyanov static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp)
582da91b54SViktor Prutyanov {
592da91b54SViktor Prutyanov     WinDumpPhyMemDesc64 *desc = &h->PhysicalMemoryBlock;
602da91b54SViktor Prutyanov     WinDumpPhyMemRun64 *run = desc->Run;
612da91b54SViktor Prutyanov     Error *local_err = NULL;
622da91b54SViktor Prutyanov     int i;
632da91b54SViktor Prutyanov 
642da91b54SViktor Prutyanov     for (i = 0; i < desc->NumberOfRuns; i++) {
652da91b54SViktor Prutyanov         s->written_size += write_run(run + i, s->fd, &local_err);
662da91b54SViktor Prutyanov         if (local_err) {
672da91b54SViktor Prutyanov             error_propagate(errp, local_err);
682da91b54SViktor Prutyanov             return;
692da91b54SViktor Prutyanov         }
702da91b54SViktor Prutyanov     }
712da91b54SViktor Prutyanov }
722da91b54SViktor Prutyanov 
732da91b54SViktor Prutyanov static void patch_mm_pfn_database(WinDumpHeader64 *h, Error **errp)
742da91b54SViktor Prutyanov {
752da91b54SViktor Prutyanov     if (cpu_memory_rw_debug(first_cpu,
762da91b54SViktor Prutyanov             h->KdDebuggerDataBlock + KDBG_MM_PFN_DATABASE_OFFSET64,
772da91b54SViktor Prutyanov             (uint8_t *)&h->PfnDatabase, sizeof(h->PfnDatabase), 0)) {
782da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to read MmPfnDatabase");
792da91b54SViktor Prutyanov         return;
802da91b54SViktor Prutyanov     }
812da91b54SViktor Prutyanov }
822da91b54SViktor Prutyanov 
832da91b54SViktor Prutyanov static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp)
842da91b54SViktor Prutyanov {
852da91b54SViktor Prutyanov     uint64_t KiBugcheckData;
862da91b54SViktor Prutyanov 
872da91b54SViktor Prutyanov     if (cpu_memory_rw_debug(first_cpu,
882da91b54SViktor Prutyanov             h->KdDebuggerDataBlock + KDBG_KI_BUGCHECK_DATA_OFFSET64,
892da91b54SViktor Prutyanov             (uint8_t *)&KiBugcheckData, sizeof(KiBugcheckData), 0)) {
902da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to read KiBugcheckData");
912da91b54SViktor Prutyanov         return;
922da91b54SViktor Prutyanov     }
932da91b54SViktor Prutyanov 
942da91b54SViktor Prutyanov     if (cpu_memory_rw_debug(first_cpu,
952da91b54SViktor Prutyanov             KiBugcheckData,
962da91b54SViktor Prutyanov             h->BugcheckData, sizeof(h->BugcheckData), 0)) {
972da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to read bugcheck data");
982da91b54SViktor Prutyanov         return;
992da91b54SViktor Prutyanov     }
1002da91b54SViktor Prutyanov }
1012da91b54SViktor Prutyanov 
1022da91b54SViktor Prutyanov /*
1032da91b54SViktor Prutyanov  * This routine tries to correct mistakes in crashdump header.
1042da91b54SViktor Prutyanov  */
1052da91b54SViktor Prutyanov static void patch_header(WinDumpHeader64 *h)
1062da91b54SViktor Prutyanov {
1072da91b54SViktor Prutyanov     Error *local_err = NULL;
1082da91b54SViktor Prutyanov 
1092da91b54SViktor Prutyanov     h->RequiredDumpSpace = sizeof(WinDumpHeader64) +
1102da91b54SViktor Prutyanov             (h->PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
1112da91b54SViktor Prutyanov     h->PhysicalMemoryBlock.unused = 0;
1122da91b54SViktor Prutyanov     h->unused1 = 0;
1132da91b54SViktor Prutyanov 
1142da91b54SViktor Prutyanov     patch_mm_pfn_database(h, &local_err);
1152da91b54SViktor Prutyanov     if (local_err) {
1162da91b54SViktor Prutyanov         warn_report_err(local_err);
1172da91b54SViktor Prutyanov         local_err = NULL;
1182da91b54SViktor Prutyanov     }
1192da91b54SViktor Prutyanov     patch_bugcheck_data(h, &local_err);
1202da91b54SViktor Prutyanov     if (local_err) {
1212da91b54SViktor Prutyanov         warn_report_err(local_err);
1222da91b54SViktor Prutyanov     }
1232da91b54SViktor Prutyanov }
1242da91b54SViktor Prutyanov 
1252da91b54SViktor Prutyanov static void check_header(WinDumpHeader64 *h, Error **errp)
1262da91b54SViktor Prutyanov {
1272da91b54SViktor Prutyanov     const char Signature[] = "PAGE";
1282da91b54SViktor Prutyanov     const char ValidDump[] = "DU64";
1292da91b54SViktor Prutyanov 
1302da91b54SViktor Prutyanov     if (memcmp(h->Signature, Signature, sizeof(h->Signature))) {
1312da91b54SViktor Prutyanov         error_setg(errp, "win-dump: invalid header, expected '%.4s',"
1322da91b54SViktor Prutyanov                          " got '%.4s'", Signature, h->Signature);
1332da91b54SViktor Prutyanov         return;
1342da91b54SViktor Prutyanov     }
1352da91b54SViktor Prutyanov 
1362da91b54SViktor Prutyanov     if (memcmp(h->ValidDump, ValidDump, sizeof(h->ValidDump))) {
1372da91b54SViktor Prutyanov         error_setg(errp, "win-dump: invalid header, expected '%.4s',"
1382da91b54SViktor Prutyanov                          " got '%.4s'", ValidDump, h->ValidDump);
1392da91b54SViktor Prutyanov         return;
1402da91b54SViktor Prutyanov     }
1412da91b54SViktor Prutyanov }
1422da91b54SViktor Prutyanov 
1432da91b54SViktor Prutyanov static void check_kdbg(WinDumpHeader64 *h, Error **errp)
1442da91b54SViktor Prutyanov {
1452da91b54SViktor Prutyanov     const char OwnerTag[] = "KDBG";
1462da91b54SViktor Prutyanov     char read_OwnerTag[4];
1472da91b54SViktor Prutyanov 
1482da91b54SViktor Prutyanov     if (cpu_memory_rw_debug(first_cpu,
1492da91b54SViktor Prutyanov             h->KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64,
1502da91b54SViktor Prutyanov             (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) {
1512da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to read OwnerTag");
1522da91b54SViktor Prutyanov         return;
1532da91b54SViktor Prutyanov     }
1542da91b54SViktor Prutyanov 
1552da91b54SViktor Prutyanov     if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) {
1562da91b54SViktor Prutyanov         error_setg(errp, "win-dump: invalid KDBG OwnerTag,"
1572da91b54SViktor Prutyanov                          " expected '%.4s', got '%.4s',"
1582da91b54SViktor Prutyanov                          " KdDebuggerDataBlock seems to be encrypted",
1592da91b54SViktor Prutyanov                          OwnerTag, read_OwnerTag);
1602da91b54SViktor Prutyanov         return;
1612da91b54SViktor Prutyanov     }
1622da91b54SViktor Prutyanov }
1632da91b54SViktor Prutyanov 
1642da91b54SViktor Prutyanov void create_win_dump(DumpState *s, Error **errp)
1652da91b54SViktor Prutyanov {
1662da91b54SViktor Prutyanov     WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note +
1672da91b54SViktor Prutyanov             VMCOREINFO_ELF_NOTE_HDR_SIZE);
168*92d1b3d5SViktor Prutyanov     X86CPU *first_x86_cpu = X86_CPU(first_cpu);
169*92d1b3d5SViktor Prutyanov     uint64_t saved_cr3 = first_x86_cpu->env.cr[3];
1702da91b54SViktor Prutyanov     Error *local_err = NULL;
1712da91b54SViktor Prutyanov 
1722da91b54SViktor Prutyanov     if (s->guest_note_size != sizeof(WinDumpHeader64) +
1732da91b54SViktor Prutyanov             VMCOREINFO_ELF_NOTE_HDR_SIZE) {
1742da91b54SViktor Prutyanov         error_setg(errp, "win-dump: invalid vmcoreinfo note size");
1752da91b54SViktor Prutyanov         return;
1762da91b54SViktor Prutyanov     }
1772da91b54SViktor Prutyanov 
1782da91b54SViktor Prutyanov     check_header(h, &local_err);
1792da91b54SViktor Prutyanov     if (local_err) {
1802da91b54SViktor Prutyanov         error_propagate(errp, local_err);
1812da91b54SViktor Prutyanov         return;
1822da91b54SViktor Prutyanov     }
1832da91b54SViktor Prutyanov 
184*92d1b3d5SViktor Prutyanov     /*
185*92d1b3d5SViktor Prutyanov      * Further access to kernel structures by virtual addresses
186*92d1b3d5SViktor Prutyanov      * should be made from system context.
187*92d1b3d5SViktor Prutyanov      */
188*92d1b3d5SViktor Prutyanov 
189*92d1b3d5SViktor Prutyanov     first_x86_cpu->env.cr[3] = h->DirectoryTableBase;
190*92d1b3d5SViktor Prutyanov 
1912da91b54SViktor Prutyanov     check_kdbg(h, &local_err);
1922da91b54SViktor Prutyanov     if (local_err) {
1932da91b54SViktor Prutyanov         error_propagate(errp, local_err);
194*92d1b3d5SViktor Prutyanov         goto out_cr3;
1952da91b54SViktor Prutyanov     }
1962da91b54SViktor Prutyanov 
1972da91b54SViktor Prutyanov     patch_header(h);
1982da91b54SViktor Prutyanov 
1992da91b54SViktor Prutyanov     s->total_size = h->RequiredDumpSpace;
2002da91b54SViktor Prutyanov 
2012da91b54SViktor Prutyanov     s->written_size = qemu_write_full(s->fd, h, sizeof(*h));
2022da91b54SViktor Prutyanov     if (s->written_size != sizeof(*h)) {
2032da91b54SViktor Prutyanov         error_setg(errp, QERR_IO_ERROR);
204*92d1b3d5SViktor Prutyanov         goto out_cr3;
2052da91b54SViktor Prutyanov     }
2062da91b54SViktor Prutyanov 
2072da91b54SViktor Prutyanov     write_runs(s, h, &local_err);
2082da91b54SViktor Prutyanov     if (local_err) {
2092da91b54SViktor Prutyanov         error_propagate(errp, local_err);
210*92d1b3d5SViktor Prutyanov         goto out_cr3;
2112da91b54SViktor Prutyanov     }
212*92d1b3d5SViktor Prutyanov 
213*92d1b3d5SViktor Prutyanov out_cr3:
214*92d1b3d5SViktor Prutyanov     first_x86_cpu->env.cr[3] = saved_cr3;
215*92d1b3d5SViktor Prutyanov 
216*92d1b3d5SViktor Prutyanov     return;
2172da91b54SViktor Prutyanov }
218