xref: /qemu/dump/win_dump.c (revision 2da91b54fe98faa8676264ac6e5a3aac5b69bec2)
1*2da91b54SViktor Prutyanov /*
2*2da91b54SViktor Prutyanov  * Windows crashdump
3*2da91b54SViktor Prutyanov  *
4*2da91b54SViktor Prutyanov  * Copyright (c) 2018 Virtuozzo International GmbH
5*2da91b54SViktor Prutyanov  *
6*2da91b54SViktor Prutyanov  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7*2da91b54SViktor Prutyanov  * See the COPYING file in the top-level directory.
8*2da91b54SViktor Prutyanov  *
9*2da91b54SViktor Prutyanov  */
10*2da91b54SViktor Prutyanov 
11*2da91b54SViktor Prutyanov #include "qemu/osdep.h"
12*2da91b54SViktor Prutyanov #include "qemu/cutils.h"
13*2da91b54SViktor Prutyanov #include "elf.h"
14*2da91b54SViktor Prutyanov #include "cpu.h"
15*2da91b54SViktor Prutyanov #include "exec/hwaddr.h"
16*2da91b54SViktor Prutyanov #include "monitor/monitor.h"
17*2da91b54SViktor Prutyanov #include "sysemu/kvm.h"
18*2da91b54SViktor Prutyanov #include "sysemu/dump.h"
19*2da91b54SViktor Prutyanov #include "sysemu/sysemu.h"
20*2da91b54SViktor Prutyanov #include "sysemu/memory_mapping.h"
21*2da91b54SViktor Prutyanov #include "sysemu/cpus.h"
22*2da91b54SViktor Prutyanov #include "qapi/error.h"
23*2da91b54SViktor Prutyanov #include "qapi/qmp/qerror.h"
24*2da91b54SViktor Prutyanov #include "qemu/error-report.h"
25*2da91b54SViktor Prutyanov #include "hw/misc/vmcoreinfo.h"
26*2da91b54SViktor Prutyanov #include "win_dump.h"
27*2da91b54SViktor Prutyanov 
28*2da91b54SViktor Prutyanov static size_t write_run(WinDumpPhyMemRun64 *run, int fd, Error **errp)
29*2da91b54SViktor Prutyanov {
30*2da91b54SViktor Prutyanov     void *buf;
31*2da91b54SViktor Prutyanov     uint64_t addr = run->BasePage << TARGET_PAGE_BITS;
32*2da91b54SViktor Prutyanov     uint64_t size = run->PageCount << TARGET_PAGE_BITS;
33*2da91b54SViktor Prutyanov     uint64_t len = size;
34*2da91b54SViktor Prutyanov 
35*2da91b54SViktor Prutyanov     buf = cpu_physical_memory_map(addr, &len, false);
36*2da91b54SViktor Prutyanov     if (!buf) {
37*2da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to map run");
38*2da91b54SViktor Prutyanov         return 0;
39*2da91b54SViktor Prutyanov     }
40*2da91b54SViktor Prutyanov     if (len != size) {
41*2da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to map entire run");
42*2da91b54SViktor Prutyanov         len = 0;
43*2da91b54SViktor Prutyanov         goto out_unmap;
44*2da91b54SViktor Prutyanov     }
45*2da91b54SViktor Prutyanov 
46*2da91b54SViktor Prutyanov     len = qemu_write_full(fd, buf, len);
47*2da91b54SViktor Prutyanov     if (len != size) {
48*2da91b54SViktor Prutyanov         error_setg(errp, QERR_IO_ERROR);
49*2da91b54SViktor Prutyanov     }
50*2da91b54SViktor Prutyanov 
51*2da91b54SViktor Prutyanov out_unmap:
52*2da91b54SViktor Prutyanov     cpu_physical_memory_unmap(buf, addr, false, len);
53*2da91b54SViktor Prutyanov 
54*2da91b54SViktor Prutyanov     return len;
55*2da91b54SViktor Prutyanov }
56*2da91b54SViktor Prutyanov 
57*2da91b54SViktor Prutyanov static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp)
58*2da91b54SViktor Prutyanov {
59*2da91b54SViktor Prutyanov     WinDumpPhyMemDesc64 *desc = &h->PhysicalMemoryBlock;
60*2da91b54SViktor Prutyanov     WinDumpPhyMemRun64 *run = desc->Run;
61*2da91b54SViktor Prutyanov     Error *local_err = NULL;
62*2da91b54SViktor Prutyanov     int i;
63*2da91b54SViktor Prutyanov 
64*2da91b54SViktor Prutyanov     for (i = 0; i < desc->NumberOfRuns; i++) {
65*2da91b54SViktor Prutyanov         s->written_size += write_run(run + i, s->fd, &local_err);
66*2da91b54SViktor Prutyanov         if (local_err) {
67*2da91b54SViktor Prutyanov             error_propagate(errp, local_err);
68*2da91b54SViktor Prutyanov             return;
69*2da91b54SViktor Prutyanov         }
70*2da91b54SViktor Prutyanov     }
71*2da91b54SViktor Prutyanov }
72*2da91b54SViktor Prutyanov 
73*2da91b54SViktor Prutyanov static void patch_mm_pfn_database(WinDumpHeader64 *h, Error **errp)
74*2da91b54SViktor Prutyanov {
75*2da91b54SViktor Prutyanov     if (cpu_memory_rw_debug(first_cpu,
76*2da91b54SViktor Prutyanov             h->KdDebuggerDataBlock + KDBG_MM_PFN_DATABASE_OFFSET64,
77*2da91b54SViktor Prutyanov             (uint8_t *)&h->PfnDatabase, sizeof(h->PfnDatabase), 0)) {
78*2da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to read MmPfnDatabase");
79*2da91b54SViktor Prutyanov         return;
80*2da91b54SViktor Prutyanov     }
81*2da91b54SViktor Prutyanov }
82*2da91b54SViktor Prutyanov 
83*2da91b54SViktor Prutyanov static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp)
84*2da91b54SViktor Prutyanov {
85*2da91b54SViktor Prutyanov     uint64_t KiBugcheckData;
86*2da91b54SViktor Prutyanov 
87*2da91b54SViktor Prutyanov     if (cpu_memory_rw_debug(first_cpu,
88*2da91b54SViktor Prutyanov             h->KdDebuggerDataBlock + KDBG_KI_BUGCHECK_DATA_OFFSET64,
89*2da91b54SViktor Prutyanov             (uint8_t *)&KiBugcheckData, sizeof(KiBugcheckData), 0)) {
90*2da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to read KiBugcheckData");
91*2da91b54SViktor Prutyanov         return;
92*2da91b54SViktor Prutyanov     }
93*2da91b54SViktor Prutyanov 
94*2da91b54SViktor Prutyanov     if (cpu_memory_rw_debug(first_cpu,
95*2da91b54SViktor Prutyanov             KiBugcheckData,
96*2da91b54SViktor Prutyanov             h->BugcheckData, sizeof(h->BugcheckData), 0)) {
97*2da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to read bugcheck data");
98*2da91b54SViktor Prutyanov         return;
99*2da91b54SViktor Prutyanov     }
100*2da91b54SViktor Prutyanov }
101*2da91b54SViktor Prutyanov 
102*2da91b54SViktor Prutyanov /*
103*2da91b54SViktor Prutyanov  * This routine tries to correct mistakes in crashdump header.
104*2da91b54SViktor Prutyanov  */
105*2da91b54SViktor Prutyanov static void patch_header(WinDumpHeader64 *h)
106*2da91b54SViktor Prutyanov {
107*2da91b54SViktor Prutyanov     Error *local_err = NULL;
108*2da91b54SViktor Prutyanov 
109*2da91b54SViktor Prutyanov     h->RequiredDumpSpace = sizeof(WinDumpHeader64) +
110*2da91b54SViktor Prutyanov             (h->PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
111*2da91b54SViktor Prutyanov     h->PhysicalMemoryBlock.unused = 0;
112*2da91b54SViktor Prutyanov     h->unused1 = 0;
113*2da91b54SViktor Prutyanov 
114*2da91b54SViktor Prutyanov     /*
115*2da91b54SViktor Prutyanov      * We assume h->DirectoryBase and current CR3 are the same when we access
116*2da91b54SViktor Prutyanov      * memory by virtual address. In other words, we suppose current context
117*2da91b54SViktor Prutyanov      * is system context. It is definetely true in case of BSOD.
118*2da91b54SViktor Prutyanov      */
119*2da91b54SViktor Prutyanov 
120*2da91b54SViktor Prutyanov     patch_mm_pfn_database(h, &local_err);
121*2da91b54SViktor Prutyanov     if (local_err) {
122*2da91b54SViktor Prutyanov         warn_report_err(local_err);
123*2da91b54SViktor Prutyanov         local_err = NULL;
124*2da91b54SViktor Prutyanov     }
125*2da91b54SViktor Prutyanov     patch_bugcheck_data(h, &local_err);
126*2da91b54SViktor Prutyanov     if (local_err) {
127*2da91b54SViktor Prutyanov         warn_report_err(local_err);
128*2da91b54SViktor Prutyanov     }
129*2da91b54SViktor Prutyanov }
130*2da91b54SViktor Prutyanov 
131*2da91b54SViktor Prutyanov static void check_header(WinDumpHeader64 *h, Error **errp)
132*2da91b54SViktor Prutyanov {
133*2da91b54SViktor Prutyanov     const char Signature[] = "PAGE";
134*2da91b54SViktor Prutyanov     const char ValidDump[] = "DU64";
135*2da91b54SViktor Prutyanov 
136*2da91b54SViktor Prutyanov     if (memcmp(h->Signature, Signature, sizeof(h->Signature))) {
137*2da91b54SViktor Prutyanov         error_setg(errp, "win-dump: invalid header, expected '%.4s',"
138*2da91b54SViktor Prutyanov                          " got '%.4s'", Signature, h->Signature);
139*2da91b54SViktor Prutyanov         return;
140*2da91b54SViktor Prutyanov     }
141*2da91b54SViktor Prutyanov 
142*2da91b54SViktor Prutyanov     if (memcmp(h->ValidDump, ValidDump, sizeof(h->ValidDump))) {
143*2da91b54SViktor Prutyanov         error_setg(errp, "win-dump: invalid header, expected '%.4s',"
144*2da91b54SViktor Prutyanov                          " got '%.4s'", ValidDump, h->ValidDump);
145*2da91b54SViktor Prutyanov         return;
146*2da91b54SViktor Prutyanov     }
147*2da91b54SViktor Prutyanov }
148*2da91b54SViktor Prutyanov 
149*2da91b54SViktor Prutyanov static void check_kdbg(WinDumpHeader64 *h, Error **errp)
150*2da91b54SViktor Prutyanov {
151*2da91b54SViktor Prutyanov     const char OwnerTag[] = "KDBG";
152*2da91b54SViktor Prutyanov     char read_OwnerTag[4];
153*2da91b54SViktor Prutyanov 
154*2da91b54SViktor Prutyanov     if (cpu_memory_rw_debug(first_cpu,
155*2da91b54SViktor Prutyanov             h->KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64,
156*2da91b54SViktor Prutyanov             (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) {
157*2da91b54SViktor Prutyanov         error_setg(errp, "win-dump: failed to read OwnerTag");
158*2da91b54SViktor Prutyanov         return;
159*2da91b54SViktor Prutyanov     }
160*2da91b54SViktor Prutyanov 
161*2da91b54SViktor Prutyanov     if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) {
162*2da91b54SViktor Prutyanov         error_setg(errp, "win-dump: invalid KDBG OwnerTag,"
163*2da91b54SViktor Prutyanov                          " expected '%.4s', got '%.4s',"
164*2da91b54SViktor Prutyanov                          " KdDebuggerDataBlock seems to be encrypted",
165*2da91b54SViktor Prutyanov                          OwnerTag, read_OwnerTag);
166*2da91b54SViktor Prutyanov         return;
167*2da91b54SViktor Prutyanov     }
168*2da91b54SViktor Prutyanov }
169*2da91b54SViktor Prutyanov 
170*2da91b54SViktor Prutyanov void create_win_dump(DumpState *s, Error **errp)
171*2da91b54SViktor Prutyanov {
172*2da91b54SViktor Prutyanov     WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note +
173*2da91b54SViktor Prutyanov             VMCOREINFO_ELF_NOTE_HDR_SIZE);
174*2da91b54SViktor Prutyanov     Error *local_err = NULL;
175*2da91b54SViktor Prutyanov 
176*2da91b54SViktor Prutyanov     if (s->guest_note_size != sizeof(WinDumpHeader64) +
177*2da91b54SViktor Prutyanov             VMCOREINFO_ELF_NOTE_HDR_SIZE) {
178*2da91b54SViktor Prutyanov         error_setg(errp, "win-dump: invalid vmcoreinfo note size");
179*2da91b54SViktor Prutyanov         return;
180*2da91b54SViktor Prutyanov     }
181*2da91b54SViktor Prutyanov 
182*2da91b54SViktor Prutyanov     check_header(h, &local_err);
183*2da91b54SViktor Prutyanov     if (local_err) {
184*2da91b54SViktor Prutyanov         error_propagate(errp, local_err);
185*2da91b54SViktor Prutyanov         return;
186*2da91b54SViktor Prutyanov     }
187*2da91b54SViktor Prutyanov 
188*2da91b54SViktor Prutyanov     check_kdbg(h, &local_err);
189*2da91b54SViktor Prutyanov     if (local_err) {
190*2da91b54SViktor Prutyanov         error_propagate(errp, local_err);
191*2da91b54SViktor Prutyanov         return;
192*2da91b54SViktor Prutyanov     }
193*2da91b54SViktor Prutyanov 
194*2da91b54SViktor Prutyanov     patch_header(h);
195*2da91b54SViktor Prutyanov 
196*2da91b54SViktor Prutyanov     s->total_size = h->RequiredDumpSpace;
197*2da91b54SViktor Prutyanov 
198*2da91b54SViktor Prutyanov     s->written_size = qemu_write_full(s->fd, h, sizeof(*h));
199*2da91b54SViktor Prutyanov     if (s->written_size != sizeof(*h)) {
200*2da91b54SViktor Prutyanov         error_setg(errp, QERR_IO_ERROR);
201*2da91b54SViktor Prutyanov         return;
202*2da91b54SViktor Prutyanov     }
203*2da91b54SViktor Prutyanov 
204*2da91b54SViktor Prutyanov     write_runs(s, h, &local_err);
205*2da91b54SViktor Prutyanov     if (local_err) {
206*2da91b54SViktor Prutyanov         error_propagate(errp, local_err);
207*2da91b54SViktor Prutyanov         return;
208*2da91b54SViktor Prutyanov     }
209*2da91b54SViktor Prutyanov }
210