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