]> git.proxmox.com Git - mirror_qemu.git/blob - win_dump.c
dump: add fallback KDBG using in Windows dump
[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 patch_mm_pfn_database(h, &local_err);
115 if (local_err) {
116 warn_report_err(local_err);
117 local_err = NULL;
118 }
119 patch_bugcheck_data(h, &local_err);
120 if (local_err) {
121 warn_report_err(local_err);
122 }
123 }
124
125 static void check_header(WinDumpHeader64 *h, Error **errp)
126 {
127 const char Signature[] = "PAGE";
128 const char ValidDump[] = "DU64";
129
130 if (memcmp(h->Signature, Signature, sizeof(h->Signature))) {
131 error_setg(errp, "win-dump: invalid header, expected '%.4s',"
132 " got '%.4s'", Signature, h->Signature);
133 return;
134 }
135
136 if (memcmp(h->ValidDump, ValidDump, sizeof(h->ValidDump))) {
137 error_setg(errp, "win-dump: invalid header, expected '%.4s',"
138 " got '%.4s'", ValidDump, h->ValidDump);
139 return;
140 }
141 }
142
143 static void check_kdbg(WinDumpHeader64 *h, Error **errp)
144 {
145 const char OwnerTag[] = "KDBG";
146 char read_OwnerTag[4];
147 uint64_t KdDebuggerDataBlock = h->KdDebuggerDataBlock;
148 bool try_fallback = true;
149
150 try_again:
151 if (cpu_memory_rw_debug(first_cpu,
152 KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64,
153 (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) {
154 error_setg(errp, "win-dump: failed to read OwnerTag");
155 return;
156 }
157
158 if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) {
159 if (try_fallback) {
160 /*
161 * If attempt to use original KDBG failed
162 * (most likely because of its encryption),
163 * we try to use KDBG obtained by guest driver.
164 */
165
166 KdDebuggerDataBlock = h->BugcheckParameter1;
167 try_fallback = false;
168 goto try_again;
169 } else {
170 error_setg(errp, "win-dump: invalid KDBG OwnerTag,"
171 " expected '%.4s', got '%.4s'",
172 OwnerTag, read_OwnerTag);
173 return;
174 }
175 }
176
177 h->KdDebuggerDataBlock = KdDebuggerDataBlock;
178 }
179
180 void create_win_dump(DumpState *s, Error **errp)
181 {
182 WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note +
183 VMCOREINFO_ELF_NOTE_HDR_SIZE);
184 X86CPU *first_x86_cpu = X86_CPU(first_cpu);
185 uint64_t saved_cr3 = first_x86_cpu->env.cr[3];
186 Error *local_err = NULL;
187
188 if (s->guest_note_size != sizeof(WinDumpHeader64) +
189 VMCOREINFO_ELF_NOTE_HDR_SIZE) {
190 error_setg(errp, "win-dump: invalid vmcoreinfo note size");
191 return;
192 }
193
194 check_header(h, &local_err);
195 if (local_err) {
196 error_propagate(errp, local_err);
197 return;
198 }
199
200 /*
201 * Further access to kernel structures by virtual addresses
202 * should be made from system context.
203 */
204
205 first_x86_cpu->env.cr[3] = h->DirectoryTableBase;
206
207 check_kdbg(h, &local_err);
208 if (local_err) {
209 error_propagate(errp, local_err);
210 goto out_cr3;
211 }
212
213 patch_header(h);
214
215 s->total_size = h->RequiredDumpSpace;
216
217 s->written_size = qemu_write_full(s->fd, h, sizeof(*h));
218 if (s->written_size != sizeof(*h)) {
219 error_setg(errp, QERR_IO_ERROR);
220 goto out_cr3;
221 }
222
223 write_runs(s, h, &local_err);
224 if (local_err) {
225 error_propagate(errp, local_err);
226 goto out_cr3;
227 }
228
229 out_cr3:
230 first_x86_cpu->env.cr[3] = saved_cr3;
231
232 return;
233 }