qemu/dump/win_dump.c
<<
>>
Prefs
   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-common.h"
  13#include "qemu/cutils.h"
  14#include "elf.h"
  15#include "exec/hwaddr.h"
  16#include "monitor/monitor.h"
  17#include "sysemu/kvm.h"
  18#include "sysemu/dump.h"
  19#include "sysemu/memory_mapping.h"
  20#include "sysemu/cpus.h"
  21#include "qapi/error.h"
  22#include "qapi/qmp/qerror.h"
  23#include "qemu/error-report.h"
  24#include "hw/misc/vmcoreinfo.h"
  25#include "win_dump.h"
  26
  27static size_t write_run(WinDumpPhyMemRun64 *run, int fd, Error **errp)
  28{
  29    void *buf;
  30    uint64_t addr = run->BasePage << TARGET_PAGE_BITS;
  31    uint64_t size = run->PageCount << TARGET_PAGE_BITS;
  32    uint64_t len, l;
  33    size_t total = 0;
  34
  35    while (size) {
  36        len = size;
  37
  38        buf = cpu_physical_memory_map(addr, &len, false);
  39        if (!buf) {
  40            error_setg(errp, "win-dump: failed to map physical range"
  41                             " 0x%016" PRIx64 "-0x%016" PRIx64, addr, addr + size - 1);
  42            return 0;
  43        }
  44
  45        l = qemu_write_full(fd, buf, len);
  46        cpu_physical_memory_unmap(buf, addr, false, len);
  47        if (l != len) {
  48            error_setg(errp, QERR_IO_ERROR);
  49            return 0;
  50        }
  51
  52        addr += l;
  53        size -= l;
  54        total += l;
  55    }
  56
  57    return total;
  58}
  59
  60static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp)
  61{
  62    WinDumpPhyMemDesc64 *desc = &h->PhysicalMemoryBlock;
  63    WinDumpPhyMemRun64 *run = desc->Run;
  64    Error *local_err = NULL;
  65    int i;
  66
  67    for (i = 0; i < desc->NumberOfRuns; i++) {
  68        s->written_size += write_run(run + i, s->fd, &local_err);
  69        if (local_err) {
  70            error_propagate(errp, local_err);
  71            return;
  72        }
  73    }
  74}
  75
  76static void patch_mm_pfn_database(WinDumpHeader64 *h, Error **errp)
  77{
  78    if (cpu_memory_rw_debug(first_cpu,
  79            h->KdDebuggerDataBlock + KDBG_MM_PFN_DATABASE_OFFSET64,
  80            (uint8_t *)&h->PfnDatabase, sizeof(h->PfnDatabase), 0)) {
  81        error_setg(errp, "win-dump: failed to read MmPfnDatabase");
  82        return;
  83    }
  84}
  85
  86static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp)
  87{
  88    uint64_t KiBugcheckData;
  89
  90    if (cpu_memory_rw_debug(first_cpu,
  91            h->KdDebuggerDataBlock + KDBG_KI_BUGCHECK_DATA_OFFSET64,
  92            (uint8_t *)&KiBugcheckData, sizeof(KiBugcheckData), 0)) {
  93        error_setg(errp, "win-dump: failed to read KiBugcheckData");
  94        return;
  95    }
  96
  97    if (cpu_memory_rw_debug(first_cpu,
  98            KiBugcheckData,
  99            h->BugcheckData, sizeof(h->BugcheckData), 0)) {
 100        error_setg(errp, "win-dump: failed to read bugcheck data");
 101        return;
 102    }
 103
 104    /*
 105     * If BugcheckCode wasn't saved, we consider guest OS as alive.
 106     */
 107
 108    if (!h->BugcheckCode) {
 109        h->BugcheckCode = LIVE_SYSTEM_DUMP;
 110    }
 111}
 112
 113/*
 114 * This routine tries to correct mistakes in crashdump header.
 115 */
 116static void patch_header(WinDumpHeader64 *h)
 117{
 118    Error *local_err = NULL;
 119
 120    h->RequiredDumpSpace = sizeof(WinDumpHeader64) +
 121            (h->PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
 122    h->PhysicalMemoryBlock.unused = 0;
 123    h->unused1 = 0;
 124
 125    patch_mm_pfn_database(h, &local_err);
 126    if (local_err) {
 127        warn_report_err(local_err);
 128        local_err = NULL;
 129    }
 130    patch_bugcheck_data(h, &local_err);
 131    if (local_err) {
 132        warn_report_err(local_err);
 133    }
 134}
 135
 136static void check_header(WinDumpHeader64 *h, Error **errp)
 137{
 138    const char Signature[] = "PAGE";
 139    const char ValidDump[] = "DU64";
 140
 141    if (memcmp(h->Signature, Signature, sizeof(h->Signature))) {
 142        error_setg(errp, "win-dump: invalid header, expected '%.4s',"
 143                         " got '%.4s'", Signature, h->Signature);
 144        return;
 145    }
 146
 147    if (memcmp(h->ValidDump, ValidDump, sizeof(h->ValidDump))) {
 148        error_setg(errp, "win-dump: invalid header, expected '%.4s',"
 149                         " got '%.4s'", ValidDump, h->ValidDump);
 150        return;
 151    }
 152}
 153
 154static void check_kdbg(WinDumpHeader64 *h, Error **errp)
 155{
 156    const char OwnerTag[] = "KDBG";
 157    char read_OwnerTag[4];
 158    uint64_t KdDebuggerDataBlock = h->KdDebuggerDataBlock;
 159    bool try_fallback = true;
 160
 161try_again:
 162    if (cpu_memory_rw_debug(first_cpu,
 163            KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64,
 164            (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) {
 165        error_setg(errp, "win-dump: failed to read OwnerTag");
 166        return;
 167    }
 168
 169    if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) {
 170        if (try_fallback) {
 171            /*
 172             * If attempt to use original KDBG failed
 173             * (most likely because of its encryption),
 174             * we try to use KDBG obtained by guest driver.
 175             */
 176
 177            KdDebuggerDataBlock = h->BugcheckParameter1;
 178            try_fallback = false;
 179            goto try_again;
 180        } else {
 181            error_setg(errp, "win-dump: invalid KDBG OwnerTag,"
 182                             " expected '%.4s', got '%.4s'",
 183                             OwnerTag, read_OwnerTag);
 184            return;
 185        }
 186    }
 187
 188    h->KdDebuggerDataBlock = KdDebuggerDataBlock;
 189}
 190
 191struct saved_context {
 192    WinContext ctx;
 193    uint64_t addr;
 194};
 195
 196static void patch_and_save_context(WinDumpHeader64 *h,
 197                                   struct saved_context *saved_ctx,
 198                                   Error **errp)
 199{
 200    uint64_t KiProcessorBlock;
 201    uint16_t OffsetPrcbContext;
 202    CPUState *cpu;
 203    int i = 0;
 204
 205    if (cpu_memory_rw_debug(first_cpu,
 206            h->KdDebuggerDataBlock + KDBG_KI_PROCESSOR_BLOCK_OFFSET64,
 207            (uint8_t *)&KiProcessorBlock, sizeof(KiProcessorBlock), 0)) {
 208        error_setg(errp, "win-dump: failed to read KiProcessorBlock");
 209        return;
 210    }
 211
 212    if (cpu_memory_rw_debug(first_cpu,
 213            h->KdDebuggerDataBlock + KDBG_OFFSET_PRCB_CONTEXT_OFFSET64,
 214            (uint8_t *)&OffsetPrcbContext, sizeof(OffsetPrcbContext), 0)) {
 215        error_setg(errp, "win-dump: failed to read OffsetPrcbContext");
 216        return;
 217    }
 218
 219    CPU_FOREACH(cpu) {
 220        X86CPU *x86_cpu = X86_CPU(cpu);
 221        CPUX86State *env = &x86_cpu->env;
 222        uint64_t Prcb;
 223        uint64_t Context;
 224        WinContext ctx;
 225
 226        if (cpu_memory_rw_debug(first_cpu,
 227                KiProcessorBlock + i * sizeof(uint64_t),
 228                (uint8_t *)&Prcb, sizeof(Prcb), 0)) {
 229            error_setg(errp, "win-dump: failed to read"
 230                             " CPU #%d PRCB location", i);
 231            return;
 232        }
 233
 234        if (cpu_memory_rw_debug(first_cpu,
 235                Prcb + OffsetPrcbContext,
 236                (uint8_t *)&Context, sizeof(Context), 0)) {
 237            error_setg(errp, "win-dump: failed to read"
 238                             " CPU #%d ContextFrame location", i);
 239            return;
 240        }
 241
 242        saved_ctx[i].addr = Context;
 243
 244        ctx = (WinContext){
 245            .ContextFlags = WIN_CTX_ALL,
 246            .MxCsr = env->mxcsr,
 247
 248            .SegEs = env->segs[0].selector,
 249            .SegCs = env->segs[1].selector,
 250            .SegSs = env->segs[2].selector,
 251            .SegDs = env->segs[3].selector,
 252            .SegFs = env->segs[4].selector,
 253            .SegGs = env->segs[5].selector,
 254            .EFlags = cpu_compute_eflags(env),
 255
 256            .Dr0 = env->dr[0],
 257            .Dr1 = env->dr[1],
 258            .Dr2 = env->dr[2],
 259            .Dr3 = env->dr[3],
 260            .Dr6 = env->dr[6],
 261            .Dr7 = env->dr[7],
 262
 263            .Rax = env->regs[R_EAX],
 264            .Rbx = env->regs[R_EBX],
 265            .Rcx = env->regs[R_ECX],
 266            .Rdx = env->regs[R_EDX],
 267            .Rsp = env->regs[R_ESP],
 268            .Rbp = env->regs[R_EBP],
 269            .Rsi = env->regs[R_ESI],
 270            .Rdi = env->regs[R_EDI],
 271            .R8  = env->regs[8],
 272            .R9  = env->regs[9],
 273            .R10 = env->regs[10],
 274            .R11 = env->regs[11],
 275            .R12 = env->regs[12],
 276            .R13 = env->regs[13],
 277            .R14 = env->regs[14],
 278            .R15 = env->regs[15],
 279
 280            .Rip = env->eip,
 281            .FltSave = {
 282                .MxCsr = env->mxcsr,
 283            },
 284        };
 285
 286        if (cpu_memory_rw_debug(first_cpu, Context,
 287                (uint8_t *)&saved_ctx[i].ctx, sizeof(WinContext), 0)) {
 288            error_setg(errp, "win-dump: failed to save CPU #%d context", i);
 289            return;
 290        }
 291
 292        if (cpu_memory_rw_debug(first_cpu, Context,
 293                (uint8_t *)&ctx, sizeof(WinContext), 1)) {
 294            error_setg(errp, "win-dump: failed to write CPU #%d context", i);
 295            return;
 296        }
 297
 298        i++;
 299    }
 300}
 301
 302static void restore_context(WinDumpHeader64 *h,
 303                            struct saved_context *saved_ctx)
 304{
 305    int i;
 306
 307    for (i = 0; i < h->NumberProcessors; i++) {
 308        if (cpu_memory_rw_debug(first_cpu, saved_ctx[i].addr,
 309                (uint8_t *)&saved_ctx[i].ctx, sizeof(WinContext), 1)) {
 310            warn_report("win-dump: failed to restore CPU #%d context", i);
 311        }
 312    }
 313}
 314
 315void create_win_dump(DumpState *s, Error **errp)
 316{
 317    WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note +
 318            VMCOREINFO_ELF_NOTE_HDR_SIZE);
 319    X86CPU *first_x86_cpu = X86_CPU(first_cpu);
 320    uint64_t saved_cr3 = first_x86_cpu->env.cr[3];
 321    struct saved_context *saved_ctx = NULL;
 322    Error *local_err = NULL;
 323
 324    if (s->guest_note_size != sizeof(WinDumpHeader64) +
 325            VMCOREINFO_ELF_NOTE_HDR_SIZE) {
 326        error_setg(errp, "win-dump: invalid vmcoreinfo note size");
 327        return;
 328    }
 329
 330    check_header(h, &local_err);
 331    if (local_err) {
 332        error_propagate(errp, local_err);
 333        return;
 334    }
 335
 336    /*
 337     * Further access to kernel structures by virtual addresses
 338     * should be made from system context.
 339     */
 340
 341    first_x86_cpu->env.cr[3] = h->DirectoryTableBase;
 342
 343    check_kdbg(h, &local_err);
 344    if (local_err) {
 345        error_propagate(errp, local_err);
 346        goto out_cr3;
 347    }
 348
 349    patch_header(h);
 350
 351    saved_ctx = g_new(struct saved_context, h->NumberProcessors);
 352
 353    /*
 354     * Always patch context because there is no way
 355     * to determine if the system-saved context is valid
 356     */
 357
 358    patch_and_save_context(h, saved_ctx, &local_err);
 359    if (local_err) {
 360        error_propagate(errp, local_err);
 361        goto out_free;
 362    }
 363
 364    s->total_size = h->RequiredDumpSpace;
 365
 366    s->written_size = qemu_write_full(s->fd, h, sizeof(*h));
 367    if (s->written_size != sizeof(*h)) {
 368        error_setg(errp, QERR_IO_ERROR);
 369        goto out_restore;
 370    }
 371
 372    write_runs(s, h, &local_err);
 373    if (local_err) {
 374        error_propagate(errp, local_err);
 375        goto out_restore;
 376    }
 377
 378out_restore:
 379    restore_context(h, saved_ctx);
 380out_free:
 381    g_free(saved_ctx);
 382out_cr3:
 383    first_x86_cpu->env.cr[3] = saved_cr3;
 384
 385    return;
 386}
 387