xref: /openbmc/qemu/dump/win_dump.c (revision df22fbb7)
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 (cpu_read_ptr(x64, first_cpu,
277                 KiProcessorBlock + i * win_dump_ptr_size(x64),
278                 &Prcb)) {
279             error_setg(errp, "win-dump: failed to read"
280                              " CPU #%d PRCB location", i);
281             return;
282         }
283 
284         if (cpu_read_ptr(x64, first_cpu,
285                 Prcb + OffsetPrcbContext,
286                 &Context)) {
287             error_setg(errp, "win-dump: failed to read"
288                              " CPU #%d ContextFrame location", i);
289             return;
290         }
291 
292         saved_ctx[i].addr = Context;
293 
294         if (x64) {
295             ctx.x64 = (WinContext64){
296                 .ContextFlags = WIN_CTX64_ALL,
297                 .MxCsr = env->mxcsr,
298 
299                 .SegEs = env->segs[0].selector,
300                 .SegCs = env->segs[1].selector,
301                 .SegSs = env->segs[2].selector,
302                 .SegDs = env->segs[3].selector,
303                 .SegFs = env->segs[4].selector,
304                 .SegGs = env->segs[5].selector,
305                 .EFlags = cpu_compute_eflags(env),
306 
307                 .Dr0 = env->dr[0],
308                 .Dr1 = env->dr[1],
309                 .Dr2 = env->dr[2],
310                 .Dr3 = env->dr[3],
311                 .Dr6 = env->dr[6],
312                 .Dr7 = env->dr[7],
313 
314                 .Rax = env->regs[R_EAX],
315                 .Rbx = env->regs[R_EBX],
316                 .Rcx = env->regs[R_ECX],
317                 .Rdx = env->regs[R_EDX],
318                 .Rsp = env->regs[R_ESP],
319                 .Rbp = env->regs[R_EBP],
320                 .Rsi = env->regs[R_ESI],
321                 .Rdi = env->regs[R_EDI],
322                 .R8  = env->regs[8],
323                 .R9  = env->regs[9],
324                 .R10 = env->regs[10],
325                 .R11 = env->regs[11],
326                 .R12 = env->regs[12],
327                 .R13 = env->regs[13],
328                 .R14 = env->regs[14],
329                 .R15 = env->regs[15],
330 
331                 .Rip = env->eip,
332                 .FltSave = {
333                     .MxCsr = env->mxcsr,
334                 },
335             };
336         } else {
337             ctx.x32 = (WinContext32){
338                 .ContextFlags = WIN_CTX32_FULL | WIN_CTX_DBG,
339 
340                 .SegEs = env->segs[0].selector,
341                 .SegCs = env->segs[1].selector,
342                 .SegSs = env->segs[2].selector,
343                 .SegDs = env->segs[3].selector,
344                 .SegFs = env->segs[4].selector,
345                 .SegGs = env->segs[5].selector,
346                 .EFlags = cpu_compute_eflags(env),
347 
348                 .Dr0 = env->dr[0],
349                 .Dr1 = env->dr[1],
350                 .Dr2 = env->dr[2],
351                 .Dr3 = env->dr[3],
352                 .Dr6 = env->dr[6],
353                 .Dr7 = env->dr[7],
354 
355                 .Eax = env->regs[R_EAX],
356                 .Ebx = env->regs[R_EBX],
357                 .Ecx = env->regs[R_ECX],
358                 .Edx = env->regs[R_EDX],
359                 .Esp = env->regs[R_ESP],
360                 .Ebp = env->regs[R_EBP],
361                 .Esi = env->regs[R_ESI],
362                 .Edi = env->regs[R_EDI],
363 
364                 .Eip = env->eip,
365             };
366         }
367 
368         if (cpu_memory_rw_debug(first_cpu, Context,
369                 &saved_ctx[i].ctx, win_dump_ctx_size(x64), 0)) {
370             error_setg(errp, "win-dump: failed to save CPU #%d context", i);
371             return;
372         }
373 
374         if (cpu_memory_rw_debug(first_cpu, Context,
375                 &ctx, win_dump_ctx_size(x64), 1)) {
376             error_setg(errp, "win-dump: failed to write CPU #%d context", i);
377             return;
378         }
379 
380         i++;
381     }
382 }
383 
384 static void restore_context(WinDumpHeader *h, bool x64,
385                             struct saved_context *saved_ctx)
386 {
387     int i;
388 
389     for (i = 0; i < WIN_DUMP_FIELD(NumberProcessors); i++) {
390         if (cpu_memory_rw_debug(first_cpu, saved_ctx[i].addr,
391                 &saved_ctx[i].ctx, win_dump_ctx_size(x64), 1)) {
392             warn_report("win-dump: failed to restore CPU #%d context", i);
393         }
394     }
395 }
396 
397 void create_win_dump(DumpState *s, Error **errp)
398 {
399     WinDumpHeader *h = (void *)(s->guest_note + VMCOREINFO_ELF_NOTE_HDR_SIZE);
400     X86CPU *first_x86_cpu = X86_CPU(first_cpu);
401     uint64_t saved_cr3 = first_x86_cpu->env.cr[3];
402     struct saved_context *saved_ctx = NULL;
403     Error *local_err = NULL;
404     bool x64 = true;
405     size_t hdr_size;
406 
407     if (s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE32 &&
408             s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE64) {
409         error_setg(errp, "win-dump: invalid vmcoreinfo note size");
410         return;
411     }
412 
413     if (!check_header(h, &x64, &local_err)) {
414         error_propagate(errp, local_err);
415         return;
416     }
417 
418     hdr_size = x64 ? sizeof(WinDumpHeader64) : sizeof(WinDumpHeader32);
419 
420     /*
421      * Further access to kernel structures by virtual addresses
422      * should be made from system context.
423      */
424 
425     first_x86_cpu->env.cr[3] = WIN_DUMP_FIELD(DirectoryTableBase);
426 
427     check_kdbg(h, x64, &local_err);
428     if (local_err) {
429         error_propagate(errp, local_err);
430         goto out_cr3;
431     }
432 
433     patch_header(h, x64);
434 
435     saved_ctx = g_new(struct saved_context, WIN_DUMP_FIELD(NumberProcessors));
436 
437     /*
438      * Always patch context because there is no way
439      * to determine if the system-saved context is valid
440      */
441 
442     patch_and_save_context(h, x64, saved_ctx, &local_err);
443     if (local_err) {
444         error_propagate(errp, local_err);
445         goto out_free;
446     }
447 
448     s->total_size = WIN_DUMP_FIELD(RequiredDumpSpace);
449 
450     s->written_size = qemu_write_full(s->fd, h, hdr_size);
451     if (s->written_size != hdr_size) {
452         error_setg(errp, QERR_IO_ERROR);
453         goto out_restore;
454     }
455 
456     write_runs(s, h, x64, &local_err);
457     if (local_err) {
458         error_propagate(errp, local_err);
459         goto out_restore;
460     }
461 
462 out_restore:
463     restore_context(h, x64, saved_ctx);
464 out_free:
465     g_free(saved_ctx);
466 out_cr3:
467     first_x86_cpu->env.cr[3] = saved_cr3;
468 
469     return;
470 }
471