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