diff --git a/sys/amd64/include/minidump.h b/sys/amd64/include/minidump.h index 1ea92b7..d21a3f6 100644 --- a/sys/amd64/include/minidump.h +++ b/sys/amd64/include/minidump.h @@ -30,7 +30,7 @@ #define _MACHINE_MINIDUMP_H_ 1 #define MINIDUMP_MAGIC "minidump FreeBSD/amd64" -#define MINIDUMP_VERSION 1 +#define MINIDUMP_VERSION 2 struct minidumphdr { char magic[24]; diff --git a/sys/amd64/amd64/minidump_machdep.c b/sys/amd64/amd64/minidump_machdep.c index a9af809..9ea3ce9 100644 --- a/sys/amd64/amd64/minidump_machdep.c +++ b/sys/amd64/amd64/minidump_machdep.c @@ -65,6 +65,7 @@ static off_t dumplo; static size_t fragsz; static void *dump_va; static size_t counter, progress; +static int bitmap_frozen = 0; CTASSERT(sizeof(*vm_page_dump) == 8); @@ -178,7 +179,7 @@ minidumpsys(struct dumperinfo *di) int error; uint64_t bits; uint64_t *pdp, *pd, *pt, pa; - int i, j, k, bit; + int i, j, k, n, bit; struct minidumphdr mdhdr; counter = 0; @@ -186,39 +187,47 @@ minidumpsys(struct dumperinfo *di) ptesize = 0; pdp = (uint64_t *)PHYS_TO_DMAP(KPDPphys); for (va = VM_MIN_KERNEL_ADDRESS; va < MAX(KERNBASE + NKPT * NBPDR, - kernel_vm_end); va += NBPDR) { + kernel_vm_end); ) { i = (va >> PDPSHIFT) & ((1ul << NPDPEPGSHIFT) - 1); /* * We always write a page, even if it is zero. Each - * page written corresponds to 2MB of space + * page written corresponds to 1GB of space */ ptesize += PAGE_SIZE; - if ((pdp[i] & PG_V) == 0) - continue; - pd = (uint64_t *)PHYS_TO_DMAP(pdp[i] & PG_FRAME); - j = ((va >> PDRSHIFT) & ((1ul << NPDEPGSHIFT) - 1)); - if ((pd[j] & (PG_PS | PG_V)) == (PG_PS | PG_V)) { - /* This is an entire 2M page. */ - pa = pd[j] & PG_PS_FRAME; - for (k = 0; k < NPTEPG; k++) { - if (is_dumpable(pa)) - dump_add_page(pa); - pa += PAGE_SIZE; - } + if ((pdp[i] & PG_V) == 0) { + va += NBPDP; continue; } - if ((pd[j] & PG_V) == PG_V) { - /* set bit for each valid page in this 2MB block */ - pt = (uint64_t *)PHYS_TO_DMAP(pd[j] & PG_FRAME); - for (k = 0; k < NPTEPG; k++) { - if ((pt[k] & PG_V) == PG_V) { - pa = pt[k] & PG_FRAME; + + pd = (uint64_t *)PHYS_TO_DMAP(pdp[i] & PG_FRAME); + for (n = 0; n < NPDEPG; n++, va += NBPDR) { + j = ((va >> PDRSHIFT) & ((1ul << NPDEPGSHIFT) - 1)); + if ((pd[j] & (PG_PS | PG_V)) == (PG_PS | PG_V)) { + /* This is an entire 2M page. */ + pa = pd[j] & PG_PS_FRAME; + for (k = 0; k < NPTEPG; k++) { if (is_dumpable(pa)) dump_add_page(pa); + pa += PAGE_SIZE; } + continue; + } + if ((pd[j] & PG_V) == PG_V) { + pa = pd[j] & PG_FRAME; + if (is_dumpable(pa)) + dump_add_page(pa); + /* set bit for each valid page in this 2MB block */ + pt = (uint64_t *)PHYS_TO_DMAP(pd[j] & PG_FRAME); + for (k = 0; k < NPTEPG; k++) { + if ((pt[k] & PG_V) == PG_V) { + pa = pt[k] & PG_FRAME; + if (is_dumpable(pa)) + dump_add_page(pa); + } + } + } else { + /* nothing, we're going to dump a null page */ } - } else { - /* nothing, we're going to dump a null page */ } } @@ -267,6 +276,12 @@ minidumpsys(struct dumperinfo *di) printf("Physical memory: %ju MB\n", ptoa((uintmax_t)physmem) / 1048576); printf("Dumping %llu MB:", (long long)dumpsize >> 20); + /* + * Freeze bitmap of pages to dump, so that disk writing routines don't + * accidentally modify it (while doing memory allocations, etc). + */ + bitmap_frozen = 1; + /* Dump leader */ error = dump_write(di, &kdh, 0, dumplo, sizeof(kdh)); if (error) @@ -290,10 +305,10 @@ minidumpsys(struct dumperinfo *di) if (error) goto fail; - /* Dump kernel page table pages */ + /* Dump kernel page directory pages */ pdp = (uint64_t *)PHYS_TO_DMAP(KPDPphys); for (va = VM_MIN_KERNEL_ADDRESS; va < MAX(KERNBASE + NKPT * NBPDR, - kernel_vm_end); va += NBPDR) { + kernel_vm_end); va += NBPDP) { i = (va >> PDPSHIFT) & ((1ul << NPDPEPGSHIFT) - 1); /* We always write a page, even if it is zero */ if ((pdp[i] & PG_V) == 0) { @@ -307,38 +322,14 @@ minidumpsys(struct dumperinfo *di) goto fail; continue; } + pd = (uint64_t *)PHYS_TO_DMAP(pdp[i] & PG_FRAME); - j = ((va >> PDRSHIFT) & ((1ul << NPDEPGSHIFT) - 1)); - if ((pd[j] & (PG_PS | PG_V)) == (PG_PS | PG_V)) { - /* This is a single 2M block. Generate a fake PTP */ - pa = pd[j] & PG_PS_FRAME; - for (k = 0; k < NPTEPG; k++) { - fakept[k] = (pa + (k * PAGE_SIZE)) | PG_V | PG_RW | PG_A | PG_M; - } - error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); - if (error) - goto fail; - /* flush, in case we reuse fakept in the same block */ - error = blk_flush(di); - if (error) - goto fail; - continue; - } - if ((pd[j] & PG_V) == PG_V) { - pt = (uint64_t *)PHYS_TO_DMAP(pd[j] & PG_FRAME); - error = blk_write(di, (char *)pt, 0, PAGE_SIZE); - if (error) - goto fail; - } else { - bzero(fakept, sizeof(fakept)); - error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); - if (error) - goto fail; - /* flush, in case we reuse fakept in the same block */ - error = blk_flush(di); - if (error) - goto fail; - } + error = blk_write(di, (char *)pd, 0, PAGE_SIZE); + if (error) + goto fail; + error = blk_flush(di); + if (error) + goto fail; } /* Dump memory chunks */ @@ -387,6 +378,9 @@ dump_add_page(vm_paddr_t pa) { int idx, bit; + if (bitmap_frozen) + return; + pa >>= PAGE_SHIFT; idx = pa >> 6; /* 2^6 = 64 */ bit = pa & 63; @@ -398,8 +392,12 @@ dump_drop_page(vm_paddr_t pa) { int idx, bit; + if (bitmap_frozen) + return; + pa >>= PAGE_SHIFT; idx = pa >> 6; /* 2^6 = 64 */ bit = pa & 63; atomic_clear_long(&vm_page_dump[idx], 1ul << bit); } + diff --git a/lib/libkvm/kvm_minidump_amd64.c b/lib/libkvm/kvm_minidump_amd64.c index 8f48f98..772f1d3 100644 --- a/lib/libkvm/kvm_minidump_amd64.c +++ b/lib/libkvm/kvm_minidump_amd64.c @@ -198,25 +198,49 @@ _kvm_minidump_initvtop(kvm_t *kd) static int _kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa) { + pt_entry_t pt[NPTEPG]; struct vmstate *vm; u_long offset; - pt_entry_t pte; + pd_entry_t pde; + pd_entry_t pte; u_long pteindex; + u_long pdeindex; int i; u_long a; off_t ofs; vm = kd->vmst; - offset = va & (PAGE_SIZE - 1); + offset = va & PAGE_MASK; if (va >= vm->hdr.kernbase) { - pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; - pte = vm->ptemap[pteindex]; - if (((u_long)pte & PG_V) == 0) { - _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); + pdeindex = (va - vm->hdr.kernbase) >> PDRSHIFT; + pde = vm->ptemap[pdeindex]; + if (((u_long)pde & PG_V) == 0) { + _kvm_err(kd, kd->program, "_kvm_vatop: pde not valid"); goto invalid; } - a = pte & PG_FRAME; + if ((pde & PG_PS) == 0) { + a = pde & PG_FRAME; + ofs = hpt_find(kd, a); + if (ofs == -1) { + _kvm_err(kd, kd->program, "_kvm_vatop: pt physical address 0x%lx not in minidump", a); + goto invalid; + } + if (pread(kd->pmfd, &pt, PAGE_SIZE, ofs) != PAGE_SIZE) { + _kvm_err(kd, kd->program, "cannot read %d bytes for pt", PAGE_SIZE); + return (-1); + } + pteindex = (va >> PAGE_SHIFT) & ((1ul << NPTEPGSHIFT) - 1); + pte = pt[pteindex]; + if (((u_long)pte & PG_V) == 0) { + _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); + goto invalid; + } + a = pte & PG_FRAME; + } else { + a = pde & PG_PS_FRAME; + a += (va & PDRMASK) ^ offset; + } ofs = hpt_find(kd, a); if (ofs == -1) { _kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%lx not in minidump", a);