xref: /qemu/dump/win_dump.c (revision 2ababfcc0e5e778c9005abb57f4bf6a036145a57)
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];
147*2ababfccSViktor Prutyanov     uint64_t KdDebuggerDataBlock = h->KdDebuggerDataBlock;
148*2ababfccSViktor Prutyanov     bool try_fallback = true;
1492da91b54SViktor Prutyanov 
150*2ababfccSViktor Prutyanov try_again:
1512da91b54SViktor Prutyanov     if (cpu_memory_rw_debug(first_cpu,
152*2ababfccSViktor Prutyanov             KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64,
1532da91b54SViktor Prutyanov             (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) {
1542da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to read OwnerTag");
1552da91b54SViktor Prutyanov         return;
1562da91b54SViktor Prutyanov     }
1572da91b54SViktor Prutyanov 
1582da91b54SViktor Prutyanov     if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) {
159*2ababfccSViktor Prutyanov         if (try_fallback) {
160*2ababfccSViktor Prutyanov             /*
161*2ababfccSViktor Prutyanov              * If attempt to use original KDBG failed
162*2ababfccSViktor Prutyanov              * (most likely because of its encryption),
163*2ababfccSViktor Prutyanov              * we try to use KDBG obtained by guest driver.
164*2ababfccSViktor Prutyanov              */
165*2ababfccSViktor Prutyanov 
166*2ababfccSViktor Prutyanov             KdDebuggerDataBlock = h->BugcheckParameter1;
167*2ababfccSViktor Prutyanov             try_fallback = false;
168*2ababfccSViktor Prutyanov             goto try_again;
169*2ababfccSViktor Prutyanov         } else {
1702da91b54SViktor Prutyanov             error_setg(errp, "win-dump: invalid KDBG OwnerTag,"
171*2ababfccSViktor Prutyanov                              " expected '%.4s', got '%.4s'",
1722da91b54SViktor Prutyanov                              OwnerTag, read_OwnerTag);
1732da91b54SViktor Prutyanov             return;
1742da91b54SViktor Prutyanov         }
1752da91b54SViktor Prutyanov     }
1762da91b54SViktor Prutyanov 
177*2ababfccSViktor Prutyanov     h->KdDebuggerDataBlock = KdDebuggerDataBlock;
178*2ababfccSViktor Prutyanov }
179*2ababfccSViktor Prutyanov 
1802da91b54SViktor Prutyanov void create_win_dump(DumpState *s, Error **errp)
1812da91b54SViktor Prutyanov {
1822da91b54SViktor Prutyanov     WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note +
1832da91b54SViktor Prutyanov             VMCOREINFO_ELF_NOTE_HDR_SIZE);
18492d1b3d5SViktor Prutyanov     X86CPU *first_x86_cpu = X86_CPU(first_cpu);
18592d1b3d5SViktor Prutyanov     uint64_t saved_cr3 = first_x86_cpu->env.cr[3];
1862da91b54SViktor Prutyanov     Error *local_err = NULL;
1872da91b54SViktor Prutyanov 
1882da91b54SViktor Prutyanov     if (s->guest_note_size != sizeof(WinDumpHeader64) +
1892da91b54SViktor Prutyanov             VMCOREINFO_ELF_NOTE_HDR_SIZE) {
1902da91b54SViktor Prutyanov         error_setg(errp, "win-dump: invalid vmcoreinfo note size");
1912da91b54SViktor Prutyanov         return;
1922da91b54SViktor Prutyanov     }
1932da91b54SViktor Prutyanov 
1942da91b54SViktor Prutyanov     check_header(h, &local_err);
1952da91b54SViktor Prutyanov     if (local_err) {
1962da91b54SViktor Prutyanov         error_propagate(errp, local_err);
1972da91b54SViktor Prutyanov         return;
1982da91b54SViktor Prutyanov     }
1992da91b54SViktor Prutyanov 
20092d1b3d5SViktor Prutyanov     /*
20192d1b3d5SViktor Prutyanov      * Further access to kernel structures by virtual addresses
20292d1b3d5SViktor Prutyanov      * should be made from system context.
20392d1b3d5SViktor Prutyanov      */
20492d1b3d5SViktor Prutyanov 
20592d1b3d5SViktor Prutyanov     first_x86_cpu->env.cr[3] = h->DirectoryTableBase;
20692d1b3d5SViktor Prutyanov 
2072da91b54SViktor Prutyanov     check_kdbg(h, &local_err);
2082da91b54SViktor Prutyanov     if (local_err) {
2092da91b54SViktor Prutyanov         error_propagate(errp, local_err);
21092d1b3d5SViktor Prutyanov         goto out_cr3;
2112da91b54SViktor Prutyanov     }
2122da91b54SViktor Prutyanov 
2132da91b54SViktor Prutyanov     patch_header(h);
2142da91b54SViktor Prutyanov 
2152da91b54SViktor Prutyanov     s->total_size = h->RequiredDumpSpace;
2162da91b54SViktor Prutyanov 
2172da91b54SViktor Prutyanov     s->written_size = qemu_write_full(s->fd, h, sizeof(*h));
2182da91b54SViktor Prutyanov     if (s->written_size != sizeof(*h)) {
2192da91b54SViktor Prutyanov         error_setg(errp, QERR_IO_ERROR);
22092d1b3d5SViktor Prutyanov         goto out_cr3;
2212da91b54SViktor Prutyanov     }
2222da91b54SViktor Prutyanov 
2232da91b54SViktor Prutyanov     write_runs(s, h, &local_err);
2242da91b54SViktor Prutyanov     if (local_err) {
2252da91b54SViktor Prutyanov         error_propagate(errp, local_err);
22692d1b3d5SViktor Prutyanov         goto out_cr3;
2272da91b54SViktor Prutyanov     }
22892d1b3d5SViktor Prutyanov 
22992d1b3d5SViktor Prutyanov out_cr3:
23092d1b3d5SViktor Prutyanov     first_x86_cpu->env.cr[3] = saved_cr3;
23192d1b3d5SViktor Prutyanov 
23292d1b3d5SViktor Prutyanov     return;
2332da91b54SViktor Prutyanov }
234