1 /* 2 * Windows crashdump (target specific implementations) 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 "sysemu/dump.h" 13 #include "qapi/error.h" 14 #include "qapi/qmp/qerror.h" 15 #include "exec/cpu-defs.h" 16 #include "hw/core/cpu.h" 17 #include "qemu/win_dump_defs.h" 18 #include "win_dump.h" 19 #include "cpu.h" 20 21 static size_t win_dump_ptr_size(bool x64) 22 { 23 return x64 ? sizeof(uint64_t) : sizeof(uint32_t); 24 } 25 26 #define _WIN_DUMP_FIELD(f) (x64 ? h->x64.f : h->x32.f) 27 #define WIN_DUMP_FIELD(field) _WIN_DUMP_FIELD(field) 28 29 #define _WIN_DUMP_FIELD_PTR(f) (x64 ? (void *)&h->x64.f : (void *)&h->x32.f) 30 #define WIN_DUMP_FIELD_PTR(field) _WIN_DUMP_FIELD_PTR(field) 31 32 #define _WIN_DUMP_FIELD_SIZE(f) (x64 ? sizeof(h->x64.f) : sizeof(h->x32.f)) 33 #define WIN_DUMP_FIELD_SIZE(field) _WIN_DUMP_FIELD_SIZE(field) 34 35 static size_t win_dump_ctx_size(bool x64) 36 { 37 return x64 ? sizeof(WinContext64) : sizeof(WinContext32); 38 } 39 40 static size_t write_run(uint64_t base_page, uint64_t page_count, 41 int fd, Error **errp) 42 { 43 void *buf; 44 uint64_t addr = base_page << TARGET_PAGE_BITS; 45 uint64_t size = page_count << TARGET_PAGE_BITS; 46 uint64_t len, l; 47 size_t total = 0; 48 49 while (size) { 50 len = size; 51 52 buf = cpu_physical_memory_map(addr, &len, false); 53 if (!buf) { 54 error_setg(errp, "win-dump: failed to map physical range" 55 " 0x%016" PRIx64 "-0x%016" PRIx64, addr, addr + size - 1); 56 return 0; 57 } 58 59 l = qemu_write_full(fd, buf, len); 60 cpu_physical_memory_unmap(buf, addr, false, len); 61 if (l != len) { 62 error_setg(errp, QERR_IO_ERROR); 63 return 0; 64 } 65 66 addr += l; 67 size -= l; 68 total += l; 69 } 70 71 return total; 72 } 73 74 static void write_runs(DumpState *s, WinDumpHeader *h, bool x64, Error **errp) 75 { 76 uint64_t BasePage, PageCount; 77 Error *local_err = NULL; 78 int i; 79 80 for (i = 0; i < WIN_DUMP_FIELD(PhysicalMemoryBlock.NumberOfRuns); i++) { 81 BasePage = WIN_DUMP_FIELD(PhysicalMemoryBlock.Run[i].BasePage); 82 PageCount = WIN_DUMP_FIELD(PhysicalMemoryBlock.Run[i].PageCount); 83 s->written_size += write_run(BasePage, PageCount, s->fd, &local_err); 84 if (local_err) { 85 error_propagate(errp, local_err); 86 return; 87 } 88 } 89 } 90 91 static int cpu_read_ptr(bool x64, CPUState *cpu, uint64_t addr, uint64_t *ptr) 92 { 93 int ret; 94 uint32_t ptr32; 95 uint64_t ptr64; 96 97 ret = cpu_memory_rw_debug(cpu, addr, x64 ? (void *)&ptr64 : (void *)&ptr32, 98 win_dump_ptr_size(x64), 0); 99 100 *ptr = x64 ? ptr64 : ptr32; 101 102 return ret; 103 } 104 105 static void patch_mm_pfn_database(WinDumpHeader *h, bool x64, Error **errp) 106 { 107 if (cpu_memory_rw_debug(first_cpu, 108 WIN_DUMP_FIELD(KdDebuggerDataBlock) + KDBG_MM_PFN_DATABASE_OFFSET, 109 WIN_DUMP_FIELD_PTR(PfnDatabase), 110 WIN_DUMP_FIELD_SIZE(PfnDatabase), 0)) { 111 error_setg(errp, "win-dump: failed to read MmPfnDatabase"); 112 return; 113 } 114 } 115 116 static void patch_bugcheck_data(WinDumpHeader *h, bool x64, Error **errp) 117 { 118 uint64_t KiBugcheckData; 119 120 if (cpu_read_ptr(x64, first_cpu, 121 WIN_DUMP_FIELD(KdDebuggerDataBlock) + KDBG_KI_BUGCHECK_DATA_OFFSET, 122 &KiBugcheckData)) { 123 error_setg(errp, "win-dump: failed to read KiBugcheckData"); 124 return; 125 } 126 127 if (cpu_memory_rw_debug(first_cpu, KiBugcheckData, 128 WIN_DUMP_FIELD(BugcheckData), 129 WIN_DUMP_FIELD_SIZE(BugcheckData), 0)) { 130 error_setg(errp, "win-dump: failed to read bugcheck data"); 131 return; 132 } 133 134 /* 135 * If BugcheckCode wasn't saved, we consider guest OS as alive. 136 */ 137 138 if (!WIN_DUMP_FIELD(BugcheckCode)) { 139 *(uint32_t *)WIN_DUMP_FIELD_PTR(BugcheckCode) = LIVE_SYSTEM_DUMP; 140 } 141 } 142 143 /* 144 * This routine tries to correct mistakes in crashdump header. 145 */ 146 static void patch_header(WinDumpHeader *h, bool x64) 147 { 148 Error *local_err = NULL; 149 150 if (x64) { 151 h->x64.RequiredDumpSpace = sizeof(WinDumpHeader64) + 152 (h->x64.PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS); 153 h->x64.PhysicalMemoryBlock.unused = 0; 154 h->x64.unused1 = 0; 155 } else { 156 h->x32.RequiredDumpSpace = sizeof(WinDumpHeader32) + 157 (h->x32.PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS); 158 } 159 160 patch_mm_pfn_database(h, x64, &local_err); 161 if (local_err) { 162 warn_report_err(local_err); 163 local_err = NULL; 164 } 165 patch_bugcheck_data(h, x64, &local_err); 166 if (local_err) { 167 warn_report_err(local_err); 168 } 169 } 170 171 static bool check_header(WinDumpHeader *h, bool *x64, Error **errp) 172 { 173 const char Signature[] = "PAGE"; 174 175 if (memcmp(h->Signature, Signature, sizeof(h->Signature))) { 176 error_setg(errp, "win-dump: invalid header, expected '%.4s'," 177 " got '%.4s'", Signature, h->Signature); 178 return false; 179 } 180 181 if (!memcmp(h->ValidDump, "DUMP", sizeof(h->ValidDump))) { 182 *x64 = false; 183 } else if (!memcmp(h->ValidDump, "DU64", sizeof(h->ValidDump))) { 184 *x64 = true; 185 } else { 186 error_setg(errp, "win-dump: invalid header, expected 'DUMP' or 'DU64'," 187 " got '%.4s'", h->ValidDump); 188 return false; 189 } 190 191 return true; 192 } 193 194 static void check_kdbg(WinDumpHeader *h, bool x64, Error **errp) 195 { 196 const char OwnerTag[] = "KDBG"; 197 char read_OwnerTag[4]; 198 uint64_t KdDebuggerDataBlock = WIN_DUMP_FIELD(KdDebuggerDataBlock); 199 bool try_fallback = true; 200 201 try_again: 202 if (cpu_memory_rw_debug(first_cpu, 203 KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET, 204 (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) { 205 error_setg(errp, "win-dump: failed to read OwnerTag"); 206 return; 207 } 208 209 if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) { 210 if (try_fallback) { 211 /* 212 * If attempt to use original KDBG failed 213 * (most likely because of its encryption), 214 * we try to use KDBG obtained by guest driver. 215 */ 216 217 KdDebuggerDataBlock = WIN_DUMP_FIELD(BugcheckParameter1); 218 try_fallback = false; 219 goto try_again; 220 } else { 221 error_setg(errp, "win-dump: invalid KDBG OwnerTag," 222 " expected '%.4s', got '%.4s'", 223 OwnerTag, read_OwnerTag); 224 return; 225 } 226 } 227 228 if (x64) { 229 h->x64.KdDebuggerDataBlock = KdDebuggerDataBlock; 230 } else { 231 h->x32.KdDebuggerDataBlock = KdDebuggerDataBlock; 232 } 233 } 234 235 struct saved_context { 236 WinContext ctx; 237 uint64_t addr; 238 }; 239 240 static void patch_and_save_context(WinDumpHeader *h, bool x64, 241 struct saved_context *saved_ctx, 242 Error **errp) 243 { 244 uint64_t KdDebuggerDataBlock = WIN_DUMP_FIELD(KdDebuggerDataBlock); 245 uint64_t KiProcessorBlock; 246 uint16_t OffsetPrcbContext; 247 CPUState *cpu; 248 int i = 0; 249 250 if (cpu_read_ptr(x64, first_cpu, 251 KdDebuggerDataBlock + KDBG_KI_PROCESSOR_BLOCK_OFFSET, 252 &KiProcessorBlock)) { 253 error_setg(errp, "win-dump: failed to read KiProcessorBlock"); 254 return; 255 } 256 257 if (cpu_memory_rw_debug(first_cpu, 258 KdDebuggerDataBlock + KDBG_OFFSET_PRCB_CONTEXT_OFFSET, 259 (uint8_t *)&OffsetPrcbContext, sizeof(OffsetPrcbContext), 0)) { 260 error_setg(errp, "win-dump: failed to read OffsetPrcbContext"); 261 return; 262 } 263 264 CPU_FOREACH(cpu) { 265 X86CPU *x86_cpu = X86_CPU(cpu); 266 CPUX86State *env = &x86_cpu->env; 267 uint64_t Prcb; 268 uint64_t Context; 269 WinContext ctx; 270 271 if (i >= WIN_DUMP_FIELD(NumberProcessors)) { 272 warn_report("win-dump: number of QEMU CPUs is bigger than" 273 " NumberProcessors (%u) in guest Windows", 274 WIN_DUMP_FIELD(NumberProcessors)); 275 return; 276 } 277 278 if (cpu_read_ptr(x64, first_cpu, 279 KiProcessorBlock + i * win_dump_ptr_size(x64), 280 &Prcb)) { 281 error_setg(errp, "win-dump: failed to read" 282 " CPU #%d PRCB location", i); 283 return; 284 } 285 286 if (cpu_read_ptr(x64, first_cpu, 287 Prcb + OffsetPrcbContext, 288 &Context)) { 289 error_setg(errp, "win-dump: failed to read" 290 " CPU #%d ContextFrame location", i); 291 return; 292 } 293 294 saved_ctx[i].addr = Context; 295 296 if (x64) { 297 ctx.x64 = (WinContext64){ 298 .ContextFlags = WIN_CTX64_ALL, 299 .MxCsr = env->mxcsr, 300 301 .SegEs = env->segs[0].selector, 302 .SegCs = env->segs[1].selector, 303 .SegSs = env->segs[2].selector, 304 .SegDs = env->segs[3].selector, 305 .SegFs = env->segs[4].selector, 306 .SegGs = env->segs[5].selector, 307 .EFlags = cpu_compute_eflags(env), 308 309 .Dr0 = env->dr[0], 310 .Dr1 = env->dr[1], 311 .Dr2 = env->dr[2], 312 .Dr3 = env->dr[3], 313 .Dr6 = env->dr[6], 314 .Dr7 = env->dr[7], 315 316 .Rax = env->regs[R_EAX], 317 .Rbx = env->regs[R_EBX], 318 .Rcx = env->regs[R_ECX], 319 .Rdx = env->regs[R_EDX], 320 .Rsp = env->regs[R_ESP], 321 .Rbp = env->regs[R_EBP], 322 .Rsi = env->regs[R_ESI], 323 .Rdi = env->regs[R_EDI], 324 .R8 = env->regs[8], 325 .R9 = env->regs[9], 326 .R10 = env->regs[10], 327 .R11 = env->regs[11], 328 .R12 = env->regs[12], 329 .R13 = env->regs[13], 330 .R14 = env->regs[14], 331 .R15 = env->regs[15], 332 333 .Rip = env->eip, 334 .FltSave = { 335 .MxCsr = env->mxcsr, 336 }, 337 }; 338 } else { 339 ctx.x32 = (WinContext32){ 340 .ContextFlags = WIN_CTX32_FULL | WIN_CTX_DBG, 341 342 .SegEs = env->segs[0].selector, 343 .SegCs = env->segs[1].selector, 344 .SegSs = env->segs[2].selector, 345 .SegDs = env->segs[3].selector, 346 .SegFs = env->segs[4].selector, 347 .SegGs = env->segs[5].selector, 348 .EFlags = cpu_compute_eflags(env), 349 350 .Dr0 = env->dr[0], 351 .Dr1 = env->dr[1], 352 .Dr2 = env->dr[2], 353 .Dr3 = env->dr[3], 354 .Dr6 = env->dr[6], 355 .Dr7 = env->dr[7], 356 357 .Eax = env->regs[R_EAX], 358 .Ebx = env->regs[R_EBX], 359 .Ecx = env->regs[R_ECX], 360 .Edx = env->regs[R_EDX], 361 .Esp = env->regs[R_ESP], 362 .Ebp = env->regs[R_EBP], 363 .Esi = env->regs[R_ESI], 364 .Edi = env->regs[R_EDI], 365 366 .Eip = env->eip, 367 }; 368 } 369 370 if (cpu_memory_rw_debug(first_cpu, Context, 371 &saved_ctx[i].ctx, win_dump_ctx_size(x64), 0)) { 372 error_setg(errp, "win-dump: failed to save CPU #%d context", i); 373 return; 374 } 375 376 if (cpu_memory_rw_debug(first_cpu, Context, 377 &ctx, win_dump_ctx_size(x64), 1)) { 378 error_setg(errp, "win-dump: failed to write CPU #%d context", i); 379 return; 380 } 381 382 i++; 383 } 384 } 385 386 static void restore_context(WinDumpHeader *h, bool x64, 387 struct saved_context *saved_ctx) 388 { 389 int i; 390 391 for (i = 0; i < WIN_DUMP_FIELD(NumberProcessors); i++) { 392 if (cpu_memory_rw_debug(first_cpu, saved_ctx[i].addr, 393 &saved_ctx[i].ctx, win_dump_ctx_size(x64), 1)) { 394 warn_report("win-dump: failed to restore CPU #%d context", i); 395 } 396 } 397 } 398 399 void create_win_dump(DumpState *s, Error **errp) 400 { 401 WinDumpHeader *h = (void *)(s->guest_note + VMCOREINFO_ELF_NOTE_HDR_SIZE); 402 X86CPU *first_x86_cpu = X86_CPU(first_cpu); 403 uint64_t saved_cr3 = first_x86_cpu->env.cr[3]; 404 struct saved_context *saved_ctx = NULL; 405 Error *local_err = NULL; 406 bool x64 = true; 407 size_t hdr_size; 408 409 if (s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE32 && 410 s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE64) { 411 error_setg(errp, "win-dump: invalid vmcoreinfo note size"); 412 return; 413 } 414 415 if (!check_header(h, &x64, &local_err)) { 416 error_propagate(errp, local_err); 417 return; 418 } 419 420 hdr_size = x64 ? sizeof(WinDumpHeader64) : sizeof(WinDumpHeader32); 421 422 /* 423 * Further access to kernel structures by virtual addresses 424 * should be made from system context. 425 */ 426 427 first_x86_cpu->env.cr[3] = WIN_DUMP_FIELD(DirectoryTableBase); 428 429 check_kdbg(h, x64, &local_err); 430 if (local_err) { 431 error_propagate(errp, local_err); 432 goto out_cr3; 433 } 434 435 patch_header(h, x64); 436 437 saved_ctx = g_new(struct saved_context, WIN_DUMP_FIELD(NumberProcessors)); 438 439 /* 440 * Always patch context because there is no way 441 * to determine if the system-saved context is valid 442 */ 443 444 patch_and_save_context(h, x64, saved_ctx, &local_err); 445 if (local_err) { 446 error_propagate(errp, local_err); 447 goto out_free; 448 } 449 450 s->total_size = WIN_DUMP_FIELD(RequiredDumpSpace); 451 452 s->written_size = qemu_write_full(s->fd, h, hdr_size); 453 if (s->written_size != hdr_size) { 454 error_setg(errp, QERR_IO_ERROR); 455 goto out_restore; 456 } 457 458 write_runs(s, h, x64, &local_err); 459 if (local_err) { 460 error_propagate(errp, local_err); 461 goto out_restore; 462 } 463 464 out_restore: 465 restore_context(h, x64, saved_ctx); 466 out_free: 467 g_free(saved_ctx); 468 out_cr3: 469 first_x86_cpu->env.cr[3] = saved_cr3; 470 471 return; 472 } 473