diff --git a/sys/amd64/include/minidump.h b/sys/amd64/include/minidump.h index 1ea92b7..ebdb865 100644 --- a/sys/amd64/include/minidump.h +++ b/sys/amd64/include/minidump.h @@ -30,14 +30,14 @@ #define _MACHINE_MINIDUMP_H_ 1 #define MINIDUMP_MAGIC "minidump FreeBSD/amd64" -#define MINIDUMP_VERSION 1 +#define MINIDUMP_VERSION 2 struct minidumphdr { char magic[24]; uint32_t version; uint32_t msgbufsize; uint32_t bitmapsize; - uint32_t ptesize; + uint32_t pdesize; uint64_t kernbase; uint64_t dmapbase; uint64_t dmapend; diff --git a/sys/amd64/amd64/minidump_machdep.c b/sys/amd64/amd64/minidump_machdep.c index a9af809..8fc4ba2 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); @@ -167,63 +168,69 @@ blk_write(struct dumperinfo *di, char *ptr, vm_paddr_t pa, size_t sz) } /* A fake page table page, to avoid having to handle both 4K and 2M pages */ -static pt_entry_t fakept[NPTEPG]; +static pd_entry_t fakepd[NPDEPG]; void minidumpsys(struct dumperinfo *di) { uint64_t dumpsize; - uint32_t ptesize; + uint32_t pdesize; vm_offset_t va; 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; /* Walk page table pages, set bits in vm_page_dump */ - ptesize = 0; + pdesize = 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; - } + pdesize += 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 */ } } /* Calculate dump size. */ - dumpsize = ptesize; + dumpsize = pdesize; dumpsize += round_page(msgbufp->msg_size); dumpsize += round_page(vm_page_dump_size); for (i = 0; i < vm_page_dump_size / sizeof(*vm_page_dump); i++) { @@ -257,7 +264,7 @@ minidumpsys(struct dumperinfo *di) mdhdr.version = MINIDUMP_VERSION; mdhdr.msgbufsize = msgbufp->msg_size; mdhdr.bitmapsize = vm_page_dump_size; - mdhdr.ptesize = ptesize; + mdhdr.pdesize = pdesize; mdhdr.kernbase = VM_MIN_KERNEL_ADDRESS; mdhdr.dmapbase = DMAP_MIN_ADDRESS; mdhdr.dmapend = DMAP_MAX_ADDRESS; @@ -267,6 +274,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) @@ -274,9 +287,9 @@ minidumpsys(struct dumperinfo *di) dumplo += sizeof(kdh); /* Dump my header */ - bzero(&fakept, sizeof(fakept)); - bcopy(&mdhdr, &fakept, sizeof(mdhdr)); - error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); + bzero(&fakepd, sizeof(fakepd)); + bcopy(&mdhdr, &fakepd, sizeof(mdhdr)); + error = blk_write(di, (char *)&fakepd, 0, PAGE_SIZE); if (error) goto fail; @@ -290,55 +303,31 @@ minidumpsys(struct dumperinfo *di) if (error) goto fail; - /* Dump kernel page table pages */ + /* Dump kernel page directory pages */ + bzero(fakepd, sizeof(fakepd)); 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) { - bzero(fakept, sizeof(fakept)); - error = blk_write(di, (char *)&fakept, 0, PAGE_SIZE); + error = blk_write(di, (char *)&fakepd, 0, PAGE_SIZE); if (error) goto fail; - /* flush, in case we reuse fakept in the same block */ + /* flush, in case we reuse fakepd in the same block */ error = blk_flush(di); if (error) 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 +376,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 +390,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..88ff0e6 100644 --- a/lib/libkvm/kvm_minidump_amd64.c +++ b/lib/libkvm/kvm_minidump_amd64.c @@ -67,7 +67,7 @@ struct vmstate { struct minidumphdr hdr; void *hpt_head[HPT_SIZE]; uint64_t *bitmap; - uint64_t *ptemap; + uint64_t *pdemap; }; static void @@ -127,8 +127,8 @@ _kvm_minidump_freevtop(kvm_t *kd) if (vm->bitmap) free(vm->bitmap); - if (vm->ptemap) - free(vm->ptemap); + if (vm->pdemap) + free(vm->pdemap); free(vm); kd->vmst = NULL; } @@ -177,17 +177,17 @@ _kvm_minidump_initvtop(kvm_t *kd) } off += round_page(vmst->hdr.bitmapsize); - vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize); - if (vmst->ptemap == NULL) { - _kvm_err(kd, kd->program, "cannot allocate %d bytes for ptemap", vmst->hdr.ptesize); + vmst->pdemap = _kvm_malloc(kd, vmst->hdr.pdesize); + if (vmst->pdemap == NULL) { + _kvm_err(kd, kd->program, "cannot allocate %d bytes for pdemap", vmst->hdr.pdesize); return (-1); } - if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) != - vmst->hdr.ptesize) { - _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize); + if (pread(kd->pmfd, vmst->pdemap, vmst->hdr.pdesize, off) != + vmst->hdr.pdesize) { + _kvm_err(kd, kd->program, "cannot read %d bytes for pdemap", vmst->hdr.pdesize); return (-1); } - off += vmst->hdr.ptesize; + off += vmst->hdr.pdesize; /* build physical address hash table for sparse pages */ inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off); @@ -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->pdemap[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);