win_dump.c 13.8 KB
Newer Older
1
/*
2
 * Windows crashdump (target specific implementations)
3 4 5 6 7 8 9 10 11 12 13 14
 *
 * Copyright (c) 2018 Virtuozzo International GmbH
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 *
 */

#include "qemu/osdep.h"
#include "sysemu/dump.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
15 16 17
#include "exec/cpu-defs.h"
#include "hw/core/cpu.h"
#include "qemu/win_dump_defs.h"
18
#include "win_dump.h"
19
#include "cpu.h"
20

21 22 23 24
static size_t win_dump_ptr_size(bool x64)
{
    return x64 ? sizeof(uint64_t) : sizeof(uint32_t);
}
25

26
#define _WIN_DUMP_FIELD(f) (x64 ? h->x64.f : h->x32.f)
27 28
#define WIN_DUMP_FIELD(field) _WIN_DUMP_FIELD(field)

29
#define _WIN_DUMP_FIELD_PTR(f) (x64 ? (void *)&h->x64.f : (void *)&h->x32.f)
30 31
#define WIN_DUMP_FIELD_PTR(field) _WIN_DUMP_FIELD_PTR(field)

32
#define _WIN_DUMP_FIELD_SIZE(f) (x64 ? sizeof(h->x64.f) : sizeof(h->x32.f))
33 34
#define WIN_DUMP_FIELD_SIZE(field) _WIN_DUMP_FIELD_SIZE(field)

35 36 37 38
static size_t win_dump_ctx_size(bool x64)
{
    return x64 ? sizeof(WinContext64) : sizeof(WinContext32);
}
39 40 41

static size_t write_run(uint64_t base_page, uint64_t page_count,
        int fd, Error **errp)
42 43
{
    void *buf;
44 45
    uint64_t addr = base_page << TARGET_PAGE_BITS;
    uint64_t size = page_count << TARGET_PAGE_BITS;
46 47
    uint64_t len, l;
    size_t total = 0;
48

49 50
    while (size) {
        len = size;
51

52 53 54 55 56 57
        buf = cpu_physical_memory_map(addr, &len, false);
        if (!buf) {
            error_setg(errp, "win-dump: failed to map physical range"
                             " 0x%016" PRIx64 "-0x%016" PRIx64, addr, addr + size - 1);
            return 0;
        }
58

59 60 61 62 63 64 65 66 67 68 69
        l = qemu_write_full(fd, buf, len);
        cpu_physical_memory_unmap(buf, addr, false, len);
        if (l != len) {
            error_setg(errp, QERR_IO_ERROR);
            return 0;
        }

        addr += l;
        size -= l;
        total += l;
    }
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 78 79
    Error *local_err = NULL;
    int i;

80 81 82 83
    for (i = 0; i < WIN_DUMP_FIELD(PhysicalMemoryBlock.NumberOfRuns); i++) {
        BasePage = WIN_DUMP_FIELD(PhysicalMemoryBlock.Run[i].BasePage);
        PageCount = WIN_DUMP_FIELD(PhysicalMemoryBlock.Run[i].PageCount);
        s->written_size += write_run(BasePage, PageCount, s->fd, &local_err);
84 85 86 87 88 89 90
        if (local_err) {
            error_propagate(errp, local_err);
            return;
        }
    }
}

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 96
    uint64_t ptr64;

97 98
    ret = cpu_memory_rw_debug(cpu, addr, x64 ? (void *)&ptr64 : (void *)&ptr32,
            win_dump_ptr_size(x64), 0);
99

100
    *ptr = x64 ? ptr64 : ptr32;
101 102 103 104

    return ret;
}

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 110
            WIN_DUMP_FIELD_PTR(PfnDatabase),
            WIN_DUMP_FIELD_SIZE(PfnDatabase), 0)) {
111 112 113 114 115
        error_setg(errp, "win-dump: failed to read MmPfnDatabase");
        return;
    }
}

116
static void patch_bugcheck_data(WinDumpHeader *h, bool x64, Error **errp)
117 118 119
{
    uint64_t KiBugcheckData;

120 121
    if (cpu_read_ptr(x64, first_cpu,
            WIN_DUMP_FIELD(KdDebuggerDataBlock) + KDBG_KI_BUGCHECK_DATA_OFFSET,
122
            &KiBugcheckData)) {
123 124 125 126
        error_setg(errp, "win-dump: failed to read KiBugcheckData");
        return;
    }

127 128 129
    if (cpu_memory_rw_debug(first_cpu, KiBugcheckData,
            WIN_DUMP_FIELD(BugcheckData),
            WIN_DUMP_FIELD_SIZE(BugcheckData), 0)) {
130 131 132
        error_setg(errp, "win-dump: failed to read bugcheck data");
        return;
    }
133 134 135 136 137

    /*
     * If BugcheckCode wasn't saved, we consider guest OS as alive.
     */

138 139
    if (!WIN_DUMP_FIELD(BugcheckCode)) {
        *(uint32_t *)WIN_DUMP_FIELD_PTR(BugcheckCode) = LIVE_SYSTEM_DUMP;
140
    }
141 142 143 144 145
}

/*
 * This routine tries to correct mistakes in crashdump header.
 */
146
static void patch_header(WinDumpHeader *h, bool x64)
147 148 149
{
    Error *local_err = NULL;

150 151 152 153 154 155 156 157 158
    if (x64) {
        h->x64.RequiredDumpSpace = sizeof(WinDumpHeader64) +
            (h->x64.PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
        h->x64.PhysicalMemoryBlock.unused = 0;
        h->x64.unused1 = 0;
    } else {
        h->x32.RequiredDumpSpace = sizeof(WinDumpHeader32) +
            (h->x32.PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
    }
159

160
    patch_mm_pfn_database(h, x64, &local_err);
161 162 163 164
    if (local_err) {
        warn_report_err(local_err);
        local_err = NULL;
    }
165
    patch_bugcheck_data(h, x64, &local_err);
166 167 168 169 170
    if (local_err) {
        warn_report_err(local_err);
    }
}

171
static bool check_header(WinDumpHeader *h, bool *x64, Error **errp)
172 173 174 175 176 177
{
    const char Signature[] = "PAGE";

    if (memcmp(h->Signature, Signature, sizeof(h->Signature))) {
        error_setg(errp, "win-dump: invalid header, expected '%.4s',"
                         " got '%.4s'", Signature, h->Signature);
178
        return false;
179 180
    }

181 182 183 184 185 186 187 188
    if (!memcmp(h->ValidDump, "DUMP", sizeof(h->ValidDump))) {
        *x64 = false;
    } else if (!memcmp(h->ValidDump, "DU64", sizeof(h->ValidDump))) {
        *x64 = true;
    } else {
        error_setg(errp, "win-dump: invalid header, expected 'DUMP' or 'DU64',"
                   " got '%.4s'", h->ValidDump);
        return false;
189
    }
190 191

    return true;
192 193
}

194
static void check_kdbg(WinDumpHeader *h, bool x64, Error **errp)
195 196 197
{
    const char OwnerTag[] = "KDBG";
    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 205 206 207 208 209
            (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) {
        error_setg(errp, "win-dump: failed to read OwnerTag");
        return;
    }

    if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) {
210 211 212 213 214 215 216
        if (try_fallback) {
            /*
             * If attempt to use original KDBG failed
             * (most likely because of its encryption),
             * we try to use KDBG obtained by guest driver.
             */

217
            KdDebuggerDataBlock = WIN_DUMP_FIELD(BugcheckParameter1);
218 219 220 221 222 223 224 225
            try_fallback = false;
            goto try_again;
        } else {
            error_setg(errp, "win-dump: invalid KDBG OwnerTag,"
                             " expected '%.4s', got '%.4s'",
                             OwnerTag, read_OwnerTag);
            return;
        }
226
    }
227

228 229 230 231 232
    if (x64) {
        h->x64.KdDebuggerDataBlock = KdDebuggerDataBlock;
    } else {
        h->x32.KdDebuggerDataBlock = KdDebuggerDataBlock;
    }
233 234
}

235
struct saved_context {
236
    WinContext ctx;
237 238 239
    uint64_t addr;
};

240
static void patch_and_save_context(WinDumpHeader *h, bool x64,
241 242 243
                                   struct saved_context *saved_ctx,
                                   Error **errp)
{
244
    uint64_t KdDebuggerDataBlock = WIN_DUMP_FIELD(KdDebuggerDataBlock);
245 246 247 248 249
    uint64_t KiProcessorBlock;
    uint16_t OffsetPrcbContext;
    CPUState *cpu;
    int i = 0;

250 251
    if (cpu_read_ptr(x64, first_cpu,
            KdDebuggerDataBlock + KDBG_KI_PROCESSOR_BLOCK_OFFSET,
252
            &KiProcessorBlock)) {
253 254 255 256 257
        error_setg(errp, "win-dump: failed to read KiProcessorBlock");
        return;
    }

    if (cpu_memory_rw_debug(first_cpu,
258
            KdDebuggerDataBlock + KDBG_OFFSET_PRCB_CONTEXT_OFFSET,
259 260 261 262 263 264 265 266 267 268
            (uint8_t *)&OffsetPrcbContext, sizeof(OffsetPrcbContext), 0)) {
        error_setg(errp, "win-dump: failed to read OffsetPrcbContext");
        return;
    }

    CPU_FOREACH(cpu) {
        X86CPU *x86_cpu = X86_CPU(cpu);
        CPUX86State *env = &x86_cpu->env;
        uint64_t Prcb;
        uint64_t Context;
269
        WinContext ctx;
270

271 272 273 274 275 276 277
        if (i >= WIN_DUMP_FIELD(NumberProcessors)) {
            warn_report("win-dump: number of QEMU CPUs is bigger than"
                        " NumberProcessors (%u) in guest Windows",
                        WIN_DUMP_FIELD(NumberProcessors));
            return;
        }

278 279
        if (cpu_read_ptr(x64, first_cpu,
                KiProcessorBlock + i * win_dump_ptr_size(x64),
280
                &Prcb)) {
281 282 283 284 285
            error_setg(errp, "win-dump: failed to read"
                             " CPU #%d PRCB location", i);
            return;
        }

286
        if (cpu_read_ptr(x64, first_cpu,
287
                Prcb + OffsetPrcbContext,
288
                &Context)) {
289 290 291 292 293 294 295
            error_setg(errp, "win-dump: failed to read"
                             " CPU #%d ContextFrame location", i);
            return;
        }

        saved_ctx[i].addr = Context;

296 297 298
        if (x64) {
            ctx.x64 = (WinContext64){
                .ContextFlags = WIN_CTX64_ALL,
299
                .MxCsr = env->mxcsr,
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368

                .SegEs = env->segs[0].selector,
                .SegCs = env->segs[1].selector,
                .SegSs = env->segs[2].selector,
                .SegDs = env->segs[3].selector,
                .SegFs = env->segs[4].selector,
                .SegGs = env->segs[5].selector,
                .EFlags = cpu_compute_eflags(env),

                .Dr0 = env->dr[0],
                .Dr1 = env->dr[1],
                .Dr2 = env->dr[2],
                .Dr3 = env->dr[3],
                .Dr6 = env->dr[6],
                .Dr7 = env->dr[7],

                .Rax = env->regs[R_EAX],
                .Rbx = env->regs[R_EBX],
                .Rcx = env->regs[R_ECX],
                .Rdx = env->regs[R_EDX],
                .Rsp = env->regs[R_ESP],
                .Rbp = env->regs[R_EBP],
                .Rsi = env->regs[R_ESI],
                .Rdi = env->regs[R_EDI],
                .R8  = env->regs[8],
                .R9  = env->regs[9],
                .R10 = env->regs[10],
                .R11 = env->regs[11],
                .R12 = env->regs[12],
                .R13 = env->regs[13],
                .R14 = env->regs[14],
                .R15 = env->regs[15],

                .Rip = env->eip,
                .FltSave = {
                    .MxCsr = env->mxcsr,
                },
            };
        } else {
            ctx.x32 = (WinContext32){
                .ContextFlags = WIN_CTX32_FULL | WIN_CTX_DBG,

                .SegEs = env->segs[0].selector,
                .SegCs = env->segs[1].selector,
                .SegSs = env->segs[2].selector,
                .SegDs = env->segs[3].selector,
                .SegFs = env->segs[4].selector,
                .SegGs = env->segs[5].selector,
                .EFlags = cpu_compute_eflags(env),

                .Dr0 = env->dr[0],
                .Dr1 = env->dr[1],
                .Dr2 = env->dr[2],
                .Dr3 = env->dr[3],
                .Dr6 = env->dr[6],
                .Dr7 = env->dr[7],

                .Eax = env->regs[R_EAX],
                .Ebx = env->regs[R_EBX],
                .Ecx = env->regs[R_ECX],
                .Edx = env->regs[R_EDX],
                .Esp = env->regs[R_ESP],
                .Ebp = env->regs[R_EBP],
                .Esi = env->regs[R_ESI],
                .Edi = env->regs[R_EDI],

                .Eip = env->eip,
            };
        }
369 370

        if (cpu_memory_rw_debug(first_cpu, Context,
371
                &saved_ctx[i].ctx, win_dump_ctx_size(x64), 0)) {
372 373 374 375 376
            error_setg(errp, "win-dump: failed to save CPU #%d context", i);
            return;
        }

        if (cpu_memory_rw_debug(first_cpu, Context,
377
                &ctx, win_dump_ctx_size(x64), 1)) {
378 379 380 381 382 383 384 385
            error_setg(errp, "win-dump: failed to write CPU #%d context", i);
            return;
        }

        i++;
    }
}

386
static void restore_context(WinDumpHeader *h, bool x64,
387 388 389 390
                            struct saved_context *saved_ctx)
{
    int i;

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 400
void create_win_dump(DumpState *s, Error **errp)
{
401
    WinDumpHeader *h = (void *)(s->guest_note + VMCOREINFO_ELF_NOTE_HDR_SIZE);
402 403
    X86CPU *first_x86_cpu = X86_CPU(first_cpu);
    uint64_t saved_cr3 = first_x86_cpu->env.cr[3];
404
    struct saved_context *saved_ctx = NULL;
405
    Error *local_err = NULL;
406 407
    bool x64 = true;
    size_t hdr_size;
408

409 410
    if (s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE32 &&
            s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE64) {
411 412 413 414
        error_setg(errp, "win-dump: invalid vmcoreinfo note size");
        return;
    }

415
    if (!check_header(h, &x64, &local_err)) {
416 417 418 419
        error_propagate(errp, local_err);
        return;
    }

420 421
    hdr_size = x64 ? sizeof(WinDumpHeader64) : sizeof(WinDumpHeader32);

422 423 424 425 426
    /*
     * Further access to kernel structures by virtual addresses
     * should be made from system context.
     */

427
    first_x86_cpu->env.cr[3] = WIN_DUMP_FIELD(DirectoryTableBase);
428

429
    check_kdbg(h, x64, &local_err);
430 431
    if (local_err) {
        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 441 442 443

    /*
     * Always patch context because there is no way
     * to determine if the system-saved context is valid
     */

444
    patch_and_save_context(h, x64, saved_ctx, &local_err);
445 446 447 448 449
    if (local_err) {
        error_propagate(errp, local_err);
        goto out_free;
    }

450
    s->total_size = WIN_DUMP_FIELD(RequiredDumpSpace);
451

452 453
    s->written_size = qemu_write_full(s->fd, h, hdr_size);
    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 460
    if (local_err) {
        error_propagate(errp, local_err);
461
        goto out_restore;
462
    }
463

464
out_restore:
465
    restore_context(h, x64, saved_ctx);
466 467
out_free:
    g_free(saved_ctx);
468 469 470 471
out_cr3:
    first_x86_cpu->env.cr[3] = saved_cr3;

    return;
472
}