qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

win_dump.c (14227B)


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