OSDN Git Service

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