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