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