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