xref: /openbmc/qemu/dump/win_dump.c (revision 7a5951f6)
1 /*
2  * Windows crashdump
3  *
4  * Copyright (c) 2018 Virtuozzo International GmbH
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  * See the COPYING file in the top-level directory.
8  *
9  */
10 
11 #include "qemu/osdep.h"
12 #include "qemu/cutils.h"
13 #include "elf.h"
14 #include "exec/hwaddr.h"
15 #include "monitor/monitor.h"
16 #include "sysemu/kvm.h"
17 #include "sysemu/dump.h"
18 #include "sysemu/memory_mapping.h"
19 #include "sysemu/cpus.h"
20 #include "qapi/error.h"
21 #include "qapi/qmp/qerror.h"
22 #include "qemu/error-report.h"
23 #include "hw/misc/vmcoreinfo.h"
24 #include "win_dump.h"
25 
26 static size_t win_dump_ptr_size(bool x64)
27 {
28     return x64 ? sizeof(uint64_t) : sizeof(uint32_t);
29 }
30 
31 #define _WIN_DUMP_FIELD(f) (x64 ? h->x64.f : h->x32.f)
32 #define WIN_DUMP_FIELD(field) _WIN_DUMP_FIELD(field)
33 
34 #define _WIN_DUMP_FIELD_PTR(f) (x64 ? (void *)&h->x64.f : (void *)&h->x32.f)
35 #define WIN_DUMP_FIELD_PTR(field) _WIN_DUMP_FIELD_PTR(field)
36 
37 #define _WIN_DUMP_FIELD_SIZE(f) (x64 ? sizeof(h->x64.f) : sizeof(h->x32.f))
38 #define WIN_DUMP_FIELD_SIZE(field) _WIN_DUMP_FIELD_SIZE(field)
39 
40 static size_t win_dump_ctx_size(bool x64)
41 {
42     return x64 ? sizeof(WinContext64) : sizeof(WinContext32);
43 }
44 
45 static size_t write_run(uint64_t base_page, uint64_t page_count,
46         int fd, Error **errp)
47 {
48     void *buf;
49     uint64_t addr = base_page << TARGET_PAGE_BITS;
50     uint64_t size = page_count << TARGET_PAGE_BITS;
51     uint64_t len, l;
52     size_t total = 0;
53 
54     while (size) {
55         len = size;
56 
57         buf = cpu_physical_memory_map(addr, &len, false);
58         if (!buf) {
59             error_setg(errp, "win-dump: failed to map physical range"
60                              " 0x%016" PRIx64 "-0x%016" PRIx64, addr, addr + size - 1);
61             return 0;
62         }
63 
64         l = qemu_write_full(fd, buf, len);
65         cpu_physical_memory_unmap(buf, addr, false, len);
66         if (l != len) {
67             error_setg(errp, QERR_IO_ERROR);
68             return 0;
69         }
70 
71         addr += l;
72         size -= l;
73         total += l;
74     }
75 
76     return total;
77 }
78 
79 static void write_runs(DumpState *s, WinDumpHeader *h, bool x64, Error **errp)
80 {
81     uint64_t BasePage, PageCount;
82     Error *local_err = NULL;
83     int i;
84 
85     for (i = 0; i < WIN_DUMP_FIELD(PhysicalMemoryBlock.NumberOfRuns); i++) {
86         BasePage = WIN_DUMP_FIELD(PhysicalMemoryBlock.Run[i].BasePage);
87         PageCount = WIN_DUMP_FIELD(PhysicalMemoryBlock.Run[i].PageCount);
88         s->written_size += write_run(BasePage, PageCount, s->fd, &local_err);
89         if (local_err) {
90             error_propagate(errp, local_err);
91             return;
92         }
93     }
94 }
95 
96 static int cpu_read_ptr(bool x64, CPUState *cpu, uint64_t addr, uint64_t *ptr)
97 {
98     int ret;
99     uint32_t ptr32;
100     uint64_t ptr64;
101 
102     ret = cpu_memory_rw_debug(cpu, addr, x64 ? (void *)&ptr64 : (void *)&ptr32,
103             win_dump_ptr_size(x64), 0);
104 
105     *ptr = x64 ? ptr64 : ptr32;
106 
107     return ret;
108 }
109 
110 static void patch_mm_pfn_database(WinDumpHeader *h, bool x64, Error **errp)
111 {
112     if (cpu_memory_rw_debug(first_cpu,
113             WIN_DUMP_FIELD(KdDebuggerDataBlock) + KDBG_MM_PFN_DATABASE_OFFSET,
114             WIN_DUMP_FIELD_PTR(PfnDatabase),
115             WIN_DUMP_FIELD_SIZE(PfnDatabase), 0)) {
116         error_setg(errp, "win-dump: failed to read MmPfnDatabase");
117         return;
118     }
119 }
120 
121 static void patch_bugcheck_data(WinDumpHeader *h, bool x64, Error **errp)
122 {
123     uint64_t KiBugcheckData;
124 
125     if (cpu_read_ptr(x64, first_cpu,
126             WIN_DUMP_FIELD(KdDebuggerDataBlock) + KDBG_KI_BUGCHECK_DATA_OFFSET,
127             &KiBugcheckData)) {
128         error_setg(errp, "win-dump: failed to read KiBugcheckData");
129         return;
130     }
131 
132     if (cpu_memory_rw_debug(first_cpu, KiBugcheckData,
133             WIN_DUMP_FIELD(BugcheckData),
134             WIN_DUMP_FIELD_SIZE(BugcheckData), 0)) {
135         error_setg(errp, "win-dump: failed to read bugcheck data");
136         return;
137     }
138 
139     /*
140      * If BugcheckCode wasn't saved, we consider guest OS as alive.
141      */
142 
143     if (!WIN_DUMP_FIELD(BugcheckCode)) {
144         *(uint32_t *)WIN_DUMP_FIELD_PTR(BugcheckCode) = LIVE_SYSTEM_DUMP;
145     }
146 }
147 
148 /*
149  * This routine tries to correct mistakes in crashdump header.
150  */
151 static void patch_header(WinDumpHeader *h, bool x64)
152 {
153     Error *local_err = NULL;
154 
155     if (x64) {
156         h->x64.RequiredDumpSpace = sizeof(WinDumpHeader64) +
157             (h->x64.PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
158         h->x64.PhysicalMemoryBlock.unused = 0;
159         h->x64.unused1 = 0;
160     } else {
161         h->x32.RequiredDumpSpace = sizeof(WinDumpHeader32) +
162             (h->x32.PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
163     }
164 
165     patch_mm_pfn_database(h, x64, &local_err);
166     if (local_err) {
167         warn_report_err(local_err);
168         local_err = NULL;
169     }
170     patch_bugcheck_data(h, x64, &local_err);
171     if (local_err) {
172         warn_report_err(local_err);
173     }
174 }
175 
176 static bool check_header(WinDumpHeader *h, bool *x64, Error **errp)
177 {
178     const char Signature[] = "PAGE";
179 
180     if (memcmp(h->Signature, Signature, sizeof(h->Signature))) {
181         error_setg(errp, "win-dump: invalid header, expected '%.4s',"
182                          " got '%.4s'", Signature, h->Signature);
183         return false;
184     }
185 
186     if (!memcmp(h->ValidDump, "DUMP", sizeof(h->ValidDump))) {
187         *x64 = false;
188     } else if (!memcmp(h->ValidDump, "DU64", sizeof(h->ValidDump))) {
189         *x64 = true;
190     } else {
191         error_setg(errp, "win-dump: invalid header, expected 'DUMP' or 'DU64',"
192                    " got '%.4s'", h->ValidDump);
193         return false;
194     }
195 
196     return true;
197 }
198 
199 static void check_kdbg(WinDumpHeader *h, bool x64, Error **errp)
200 {
201     const char OwnerTag[] = "KDBG";
202     char read_OwnerTag[4];
203     uint64_t KdDebuggerDataBlock = WIN_DUMP_FIELD(KdDebuggerDataBlock);
204     bool try_fallback = true;
205 
206 try_again:
207     if (cpu_memory_rw_debug(first_cpu,
208             KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET,
209             (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) {
210         error_setg(errp, "win-dump: failed to read OwnerTag");
211         return;
212     }
213 
214     if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) {
215         if (try_fallback) {
216             /*
217              * If attempt to use original KDBG failed
218              * (most likely because of its encryption),
219              * we try to use KDBG obtained by guest driver.
220              */
221 
222             KdDebuggerDataBlock = WIN_DUMP_FIELD(BugcheckParameter1);
223             try_fallback = false;
224             goto try_again;
225         } else {
226             error_setg(errp, "win-dump: invalid KDBG OwnerTag,"
227                              " expected '%.4s', got '%.4s'",
228                              OwnerTag, read_OwnerTag);
229             return;
230         }
231     }
232 
233     if (x64) {
234         h->x64.KdDebuggerDataBlock = KdDebuggerDataBlock;
235     } else {
236         h->x32.KdDebuggerDataBlock = KdDebuggerDataBlock;
237     }
238 }
239 
240 struct saved_context {
241     WinContext ctx;
242     uint64_t addr;
243 };
244 
245 static void patch_and_save_context(WinDumpHeader *h, bool x64,
246                                    struct saved_context *saved_ctx,
247                                    Error **errp)
248 {
249     uint64_t KdDebuggerDataBlock = WIN_DUMP_FIELD(KdDebuggerDataBlock);
250     uint64_t KiProcessorBlock;
251     uint16_t OffsetPrcbContext;
252     CPUState *cpu;
253     int i = 0;
254 
255     if (cpu_read_ptr(x64, first_cpu,
256             KdDebuggerDataBlock + KDBG_KI_PROCESSOR_BLOCK_OFFSET,
257             &KiProcessorBlock)) {
258         error_setg(errp, "win-dump: failed to read KiProcessorBlock");
259         return;
260     }
261 
262     if (cpu_memory_rw_debug(first_cpu,
263             KdDebuggerDataBlock + KDBG_OFFSET_PRCB_CONTEXT_OFFSET,
264             (uint8_t *)&OffsetPrcbContext, sizeof(OffsetPrcbContext), 0)) {
265         error_setg(errp, "win-dump: failed to read OffsetPrcbContext");
266         return;
267     }
268 
269     CPU_FOREACH(cpu) {
270         X86CPU *x86_cpu = X86_CPU(cpu);
271         CPUX86State *env = &x86_cpu->env;
272         uint64_t Prcb;
273         uint64_t Context;
274         WinContext ctx;
275 
276         if (i >= WIN_DUMP_FIELD(NumberProcessors)) {
277             warn_report("win-dump: number of QEMU CPUs is bigger than"
278                         " NumberProcessors (%u) in guest Windows",
279                         WIN_DUMP_FIELD(NumberProcessors));
280             return;
281         }
282 
283         if (cpu_read_ptr(x64, first_cpu,
284                 KiProcessorBlock + i * win_dump_ptr_size(x64),
285                 &Prcb)) {
286             error_setg(errp, "win-dump: failed to read"
287                              " CPU #%d PRCB location", i);
288             return;
289         }
290 
291         if (cpu_read_ptr(x64, first_cpu,
292                 Prcb + OffsetPrcbContext,
293                 &Context)) {
294             error_setg(errp, "win-dump: failed to read"
295                              " CPU #%d ContextFrame location", i);
296             return;
297         }
298 
299         saved_ctx[i].addr = Context;
300 
301         if (x64) {
302             ctx.x64 = (WinContext64){
303                 .ContextFlags = WIN_CTX64_ALL,
304                 .MxCsr = env->mxcsr,
305 
306                 .SegEs = env->segs[0].selector,
307                 .SegCs = env->segs[1].selector,
308                 .SegSs = env->segs[2].selector,
309                 .SegDs = env->segs[3].selector,
310                 .SegFs = env->segs[4].selector,
311                 .SegGs = env->segs[5].selector,
312                 .EFlags = cpu_compute_eflags(env),
313 
314                 .Dr0 = env->dr[0],
315                 .Dr1 = env->dr[1],
316                 .Dr2 = env->dr[2],
317                 .Dr3 = env->dr[3],
318                 .Dr6 = env->dr[6],
319                 .Dr7 = env->dr[7],
320 
321                 .Rax = env->regs[R_EAX],
322                 .Rbx = env->regs[R_EBX],
323                 .Rcx = env->regs[R_ECX],
324                 .Rdx = env->regs[R_EDX],
325                 .Rsp = env->regs[R_ESP],
326                 .Rbp = env->regs[R_EBP],
327                 .Rsi = env->regs[R_ESI],
328                 .Rdi = env->regs[R_EDI],
329                 .R8  = env->regs[8],
330                 .R9  = env->regs[9],
331                 .R10 = env->regs[10],
332                 .R11 = env->regs[11],
333                 .R12 = env->regs[12],
334                 .R13 = env->regs[13],
335                 .R14 = env->regs[14],
336                 .R15 = env->regs[15],
337 
338                 .Rip = env->eip,
339                 .FltSave = {
340                     .MxCsr = env->mxcsr,
341                 },
342             };
343         } else {
344             ctx.x32 = (WinContext32){
345                 .ContextFlags = WIN_CTX32_FULL | WIN_CTX_DBG,
346 
347                 .SegEs = env->segs[0].selector,
348                 .SegCs = env->segs[1].selector,
349                 .SegSs = env->segs[2].selector,
350                 .SegDs = env->segs[3].selector,
351                 .SegFs = env->segs[4].selector,
352                 .SegGs = env->segs[5].selector,
353                 .EFlags = cpu_compute_eflags(env),
354 
355                 .Dr0 = env->dr[0],
356                 .Dr1 = env->dr[1],
357                 .Dr2 = env->dr[2],
358                 .Dr3 = env->dr[3],
359                 .Dr6 = env->dr[6],
360                 .Dr7 = env->dr[7],
361 
362                 .Eax = env->regs[R_EAX],
363                 .Ebx = env->regs[R_EBX],
364                 .Ecx = env->regs[R_ECX],
365                 .Edx = env->regs[R_EDX],
366                 .Esp = env->regs[R_ESP],
367                 .Ebp = env->regs[R_EBP],
368                 .Esi = env->regs[R_ESI],
369                 .Edi = env->regs[R_EDI],
370 
371                 .Eip = env->eip,
372             };
373         }
374 
375         if (cpu_memory_rw_debug(first_cpu, Context,
376                 &saved_ctx[i].ctx, win_dump_ctx_size(x64), 0)) {
377             error_setg(errp, "win-dump: failed to save CPU #%d context", i);
378             return;
379         }
380 
381         if (cpu_memory_rw_debug(first_cpu, Context,
382                 &ctx, win_dump_ctx_size(x64), 1)) {
383             error_setg(errp, "win-dump: failed to write CPU #%d context", i);
384             return;
385         }
386 
387         i++;
388     }
389 }
390 
391 static void restore_context(WinDumpHeader *h, bool x64,
392                             struct saved_context *saved_ctx)
393 {
394     int i;
395 
396     for (i = 0; i < WIN_DUMP_FIELD(NumberProcessors); i++) {
397         if (cpu_memory_rw_debug(first_cpu, saved_ctx[i].addr,
398                 &saved_ctx[i].ctx, win_dump_ctx_size(x64), 1)) {
399             warn_report("win-dump: failed to restore CPU #%d context", i);
400         }
401     }
402 }
403 
404 void create_win_dump(DumpState *s, Error **errp)
405 {
406     WinDumpHeader *h = (void *)(s->guest_note + VMCOREINFO_ELF_NOTE_HDR_SIZE);
407     X86CPU *first_x86_cpu = X86_CPU(first_cpu);
408     uint64_t saved_cr3 = first_x86_cpu->env.cr[3];
409     struct saved_context *saved_ctx = NULL;
410     Error *local_err = NULL;
411     bool x64 = true;
412     size_t hdr_size;
413 
414     if (s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE32 &&
415             s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE64) {
416         error_setg(errp, "win-dump: invalid vmcoreinfo note size");
417         return;
418     }
419 
420     if (!check_header(h, &x64, &local_err)) {
421         error_propagate(errp, local_err);
422         return;
423     }
424 
425     hdr_size = x64 ? sizeof(WinDumpHeader64) : sizeof(WinDumpHeader32);
426 
427     /*
428      * Further access to kernel structures by virtual addresses
429      * should be made from system context.
430      */
431 
432     first_x86_cpu->env.cr[3] = WIN_DUMP_FIELD(DirectoryTableBase);
433 
434     check_kdbg(h, x64, &local_err);
435     if (local_err) {
436         error_propagate(errp, local_err);
437         goto out_cr3;
438     }
439 
440     patch_header(h, x64);
441 
442     saved_ctx = g_new(struct saved_context, WIN_DUMP_FIELD(NumberProcessors));
443 
444     /*
445      * Always patch context because there is no way
446      * to determine if the system-saved context is valid
447      */
448 
449     patch_and_save_context(h, x64, saved_ctx, &local_err);
450     if (local_err) {
451         error_propagate(errp, local_err);
452         goto out_free;
453     }
454 
455     s->total_size = WIN_DUMP_FIELD(RequiredDumpSpace);
456 
457     s->written_size = qemu_write_full(s->fd, h, hdr_size);
458     if (s->written_size != hdr_size) {
459         error_setg(errp, QERR_IO_ERROR);
460         goto out_restore;
461     }
462 
463     write_runs(s, h, x64, &local_err);
464     if (local_err) {
465         error_propagate(errp, local_err);
466         goto out_restore;
467     }
468 
469 out_restore:
470     restore_context(h, x64, saved_ctx);
471 out_free:
472     g_free(saved_ctx);
473 out_cr3:
474     first_x86_cpu->env.cr[3] = saved_cr3;
475 
476     return;
477 }
478