cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

win_dump.c (10980B)


      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}