]> git.proxmox.com Git - mirror_qemu.git/blame - win_dump.c
dump: add Windows dump format to dump-guest-memory
[mirror_qemu.git] / win_dump.c
CommitLineData
2da91b54
VP
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 "cpu.h"
15#include "exec/hwaddr.h"
16#include "monitor/monitor.h"
17#include "sysemu/kvm.h"
18#include "sysemu/dump.h"
19#include "sysemu/sysemu.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
28static 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 = size;
34
35 buf = cpu_physical_memory_map(addr, &len, false);
36 if (!buf) {
37 error_setg(errp, "win-dump: failed to map run");
38 return 0;
39 }
40 if (len != size) {
41 error_setg(errp, "win-dump: failed to map entire run");
42 len = 0;
43 goto out_unmap;
44 }
45
46 len = qemu_write_full(fd, buf, len);
47 if (len != size) {
48 error_setg(errp, QERR_IO_ERROR);
49 }
50
51out_unmap:
52 cpu_physical_memory_unmap(buf, addr, false, len);
53
54 return len;
55}
56
57static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp)
58{
59 WinDumpPhyMemDesc64 *desc = &h->PhysicalMemoryBlock;
60 WinDumpPhyMemRun64 *run = desc->Run;
61 Error *local_err = NULL;
62 int i;
63
64 for (i = 0; i < desc->NumberOfRuns; i++) {
65 s->written_size += write_run(run + i, s->fd, &local_err);
66 if (local_err) {
67 error_propagate(errp, local_err);
68 return;
69 }
70 }
71}
72
73static void patch_mm_pfn_database(WinDumpHeader64 *h, Error **errp)
74{
75 if (cpu_memory_rw_debug(first_cpu,
76 h->KdDebuggerDataBlock + KDBG_MM_PFN_DATABASE_OFFSET64,
77 (uint8_t *)&h->PfnDatabase, sizeof(h->PfnDatabase), 0)) {
78 error_setg(errp, "win-dump: failed to read MmPfnDatabase");
79 return;
80 }
81}
82
83static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp)
84{
85 uint64_t KiBugcheckData;
86
87 if (cpu_memory_rw_debug(first_cpu,
88 h->KdDebuggerDataBlock + KDBG_KI_BUGCHECK_DATA_OFFSET64,
89 (uint8_t *)&KiBugcheckData, sizeof(KiBugcheckData), 0)) {
90 error_setg(errp, "win-dump: failed to read KiBugcheckData");
91 return;
92 }
93
94 if (cpu_memory_rw_debug(first_cpu,
95 KiBugcheckData,
96 h->BugcheckData, sizeof(h->BugcheckData), 0)) {
97 error_setg(errp, "win-dump: failed to read bugcheck data");
98 return;
99 }
100}
101
102/*
103 * This routine tries to correct mistakes in crashdump header.
104 */
105static void patch_header(WinDumpHeader64 *h)
106{
107 Error *local_err = NULL;
108
109 h->RequiredDumpSpace = sizeof(WinDumpHeader64) +
110 (h->PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
111 h->PhysicalMemoryBlock.unused = 0;
112 h->unused1 = 0;
113
114 /*
115 * We assume h->DirectoryBase and current CR3 are the same when we access
116 * memory by virtual address. In other words, we suppose current context
117 * is system context. It is definetely true in case of BSOD.
118 */
119
120 patch_mm_pfn_database(h, &local_err);
121 if (local_err) {
122 warn_report_err(local_err);
123 local_err = NULL;
124 }
125 patch_bugcheck_data(h, &local_err);
126 if (local_err) {
127 warn_report_err(local_err);
128 }
129}
130
131static void check_header(WinDumpHeader64 *h, Error **errp)
132{
133 const char Signature[] = "PAGE";
134 const char ValidDump[] = "DU64";
135
136 if (memcmp(h->Signature, Signature, sizeof(h->Signature))) {
137 error_setg(errp, "win-dump: invalid header, expected '%.4s',"
138 " got '%.4s'", Signature, h->Signature);
139 return;
140 }
141
142 if (memcmp(h->ValidDump, ValidDump, sizeof(h->ValidDump))) {
143 error_setg(errp, "win-dump: invalid header, expected '%.4s',"
144 " got '%.4s'", ValidDump, h->ValidDump);
145 return;
146 }
147}
148
149static void check_kdbg(WinDumpHeader64 *h, Error **errp)
150{
151 const char OwnerTag[] = "KDBG";
152 char read_OwnerTag[4];
153
154 if (cpu_memory_rw_debug(first_cpu,
155 h->KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64,
156 (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) {
157 error_setg(errp, "win-dump: failed to read OwnerTag");
158 return;
159 }
160
161 if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) {
162 error_setg(errp, "win-dump: invalid KDBG OwnerTag,"
163 " expected '%.4s', got '%.4s',"
164 " KdDebuggerDataBlock seems to be encrypted",
165 OwnerTag, read_OwnerTag);
166 return;
167 }
168}
169
170void create_win_dump(DumpState *s, Error **errp)
171{
172 WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note +
173 VMCOREINFO_ELF_NOTE_HDR_SIZE);
174 Error *local_err = NULL;
175
176 if (s->guest_note_size != sizeof(WinDumpHeader64) +
177 VMCOREINFO_ELF_NOTE_HDR_SIZE) {
178 error_setg(errp, "win-dump: invalid vmcoreinfo note size");
179 return;
180 }
181
182 check_header(h, &local_err);
183 if (local_err) {
184 error_propagate(errp, local_err);
185 return;
186 }
187
188 check_kdbg(h, &local_err);
189 if (local_err) {
190 error_propagate(errp, local_err);
191 return;
192 }
193
194 patch_header(h);
195
196 s->total_size = h->RequiredDumpSpace;
197
198 s->written_size = qemu_write_full(s->fd, h, sizeof(*h));
199 if (s->written_size != sizeof(*h)) {
200 error_setg(errp, QERR_IO_ERROR);
201 return;
202 }
203
204 write_runs(s, h, &local_err);
205 if (local_err) {
206 error_propagate(errp, local_err);
207 return;
208 }
209}