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