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