]> git.proxmox.com Git - mirror_qemu.git/blob - win_dump.c
58255c12ee187d7bd28340267e78403a41696fb8
[mirror_qemu.git] / 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 "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
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 = 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
51 out_unmap:
52 cpu_physical_memory_unmap(buf, addr, false, len);
53
54 return len;
55 }
56
57 static 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
73 static 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
83 static 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 */
105 static 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
131 static 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
149 static 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
170 void 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 }