4 * Copyright (c) 2018 Virtuozzo International GmbH
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.
11 #include "qemu/osdep.h"
12 #include "qemu/cutils.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"
26 #define WIN_DUMP_PTR_SIZE sizeof(uint64_t)
28 #define _WIN_DUMP_FIELD(f) (h->f)
29 #define WIN_DUMP_FIELD(field) _WIN_DUMP_FIELD(field)
31 #define _WIN_DUMP_FIELD_PTR(f) ((void *)&h->f)
32 #define WIN_DUMP_FIELD_PTR(field) _WIN_DUMP_FIELD_PTR(field)
34 #define _WIN_DUMP_FIELD_SIZE(f) sizeof(h->f)
35 #define WIN_DUMP_FIELD_SIZE(field) _WIN_DUMP_FIELD_SIZE(field)
37 #define WIN_DUMP_CTX_SIZE sizeof(WinContext64)
39 static size_t write_run(uint64_t base_page, uint64_t page_count,
43 uint64_t addr = base_page << TARGET_PAGE_BITS;
44 uint64_t size = page_count << TARGET_PAGE_BITS;
51 buf = cpu_physical_memory_map(addr, &len, false);
53 error_setg(errp, "win-dump: failed to map physical range"
54 " 0x%016" PRIx64 "-0x%016" PRIx64, addr, addr + size - 1);
58 l = qemu_write_full(fd, buf, len);
59 cpu_physical_memory_unmap(buf, addr, false, len);
61 error_setg(errp, QERR_IO_ERROR);
73 static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp)
75 uint64_t BasePage, PageCount;
76 Error *local_err = NULL;
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);
84 error_propagate(errp, local_err);
90 static int cpu_read_ptr(CPUState *cpu, uint64_t addr, uint64_t *ptr)
95 ret = cpu_memory_rw_debug(cpu, addr, &ptr64, WIN_DUMP_PTR_SIZE, 0);
102 static void patch_mm_pfn_database(WinDumpHeader64 *h, Error **errp)
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");
113 static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp)
115 uint64_t KiBugcheckData;
117 if (cpu_read_ptr(first_cpu,
118 WIN_DUMP_FIELD(KdDebuggerDataBlock) +
119 KDBG_KI_BUGCHECK_DATA_OFFSET64,
121 error_setg(errp, "win-dump: failed to read KiBugcheckData");
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");
133 * If BugcheckCode wasn't saved, we consider guest OS as alive.
136 if (!WIN_DUMP_FIELD(BugcheckCode)) {
137 *(uint32_t *)WIN_DUMP_FIELD_PTR(BugcheckCode) = LIVE_SYSTEM_DUMP;
142 * This routine tries to correct mistakes in crashdump header.
144 static void patch_header(WinDumpHeader64 *h)
146 Error *local_err = NULL;
148 h->RequiredDumpSpace = sizeof(WinDumpHeader64) +
149 (h->PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
150 h->PhysicalMemoryBlock.unused = 0;
153 patch_mm_pfn_database(h, &local_err);
155 warn_report_err(local_err);
158 patch_bugcheck_data(h, &local_err);
160 warn_report_err(local_err);
164 static void check_header(WinDumpHeader64 *h, Error **errp)
166 const char Signature[] = "PAGE";
167 const char ValidDump[] = "DU64";
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);
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);
182 static void check_kdbg(WinDumpHeader64 *h, Error **errp)
184 const char OwnerTag[] = "KDBG";
185 char read_OwnerTag[4];
186 uint64_t KdDebuggerDataBlock = WIN_DUMP_FIELD(KdDebuggerDataBlock);
187 bool try_fallback = true;
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");
197 if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) {
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.
205 KdDebuggerDataBlock = WIN_DUMP_FIELD(BugcheckParameter1);
206 try_fallback = false;
209 error_setg(errp, "win-dump: invalid KDBG OwnerTag,"
210 " expected '%.4s', got '%.4s'",
211 OwnerTag, read_OwnerTag);
216 h->KdDebuggerDataBlock = KdDebuggerDataBlock;
219 struct saved_context {
224 static void patch_and_save_context(WinDumpHeader64 *h,
225 struct saved_context *saved_ctx,
228 uint64_t KdDebuggerDataBlock = WIN_DUMP_FIELD(KdDebuggerDataBlock);
229 uint64_t KiProcessorBlock;
230 uint16_t OffsetPrcbContext;
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");
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");
249 X86CPU *x86_cpu = X86_CPU(cpu);
250 CPUX86State *env = &x86_cpu->env;
255 if (cpu_read_ptr(first_cpu,
256 KiProcessorBlock + i * WIN_DUMP_PTR_SIZE,
258 error_setg(errp, "win-dump: failed to read"
259 " CPU #%d PRCB location", i);
263 if (cpu_read_ptr(first_cpu,
264 Prcb + OffsetPrcbContext,
266 error_setg(errp, "win-dump: failed to read"
267 " CPU #%d ContextFrame location", i);
271 saved_ctx[i].addr = Context;
273 ctx = (WinContext64){
274 .ContextFlags = WIN_CTX64_ALL,
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),
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],
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],
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);
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);
331 static void restore_context(WinDumpHeader64 *h,
332 struct saved_context *saved_ctx)
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);
344 void create_win_dump(DumpState *s, Error **errp)
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;
353 if (s->guest_note_size != sizeof(WinDumpHeader64) +
354 VMCOREINFO_ELF_NOTE_HDR_SIZE) {
355 error_setg(errp, "win-dump: invalid vmcoreinfo note size");
359 check_header(h, &local_err);
361 error_propagate(errp, local_err);
366 * Further access to kernel structures by virtual addresses
367 * should be made from system context.
370 first_x86_cpu->env.cr[3] = WIN_DUMP_FIELD(DirectoryTableBase);
372 check_kdbg(h, &local_err);
374 error_propagate(errp, local_err);
380 saved_ctx = g_new(struct saved_context, WIN_DUMP_FIELD(NumberProcessors));
383 * Always patch context because there is no way
384 * to determine if the system-saved context is valid
387 patch_and_save_context(h, saved_ctx, &local_err);
389 error_propagate(errp, local_err);
393 s->total_size = WIN_DUMP_FIELD(RequiredDumpSpace);
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);
401 write_runs(s, h, &local_err);
403 error_propagate(errp, local_err);
408 restore_context(h, saved_ctx);
412 first_x86_cpu->env.cr[3] = saved_cr3;