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..1dd3624 100644 --- a/sys/amd64/amd64/minidump_machdep.c +++ b/sys/amd64/amd64/minidump_machdep.c @@ -167,63 +167,74 @@ 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; + int retry_count; struct minidumphdr mdhdr; + retry_count = 0; + retry: + retry_count++; 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; + /* set bit for this PTE page */ + if (is_dumpable(pa)) + dump_add_page(pa); + /* and 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++) { @@ -244,7 +255,7 @@ minidumpsys(struct dumperinfo *di) /* Determine dump offset on device. */ if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; + error = E2BIG; goto fail; } dumplo = di->mediaoffset + di->mediasize - dumpsize; @@ -257,7 +268,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; @@ -274,9 +285,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 +301,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 */ @@ -374,12 +361,21 @@ minidumpsys(struct dumperinfo *di) if (error < 0) error = -error; - if (error == ECANCELED) - printf("\nDump aborted\n"); - else if (error == ENOSPC) - printf("\nDump failed. Partition too small.\n"); + printf("\n"); + if (error == ENOSPC) { + printf("Dump map grown while dumping. "); + if (retry_count < 5) { + printf("Retrying...\n"); + goto retry; + } + printf("Dump failed.\n"); + } + else if (error == ECANCELED) + printf("Dump aborted\n"); + else if (error == E2BIG) + printf("Dump failed. Partition too small.\n"); else - printf("\n** DUMP FAILED (ERROR %d) **\n", error); + printf("** DUMP FAILED (ERROR %d) **\n", error); } void @@ -403,3 +399,4 @@ dump_drop_page(vm_paddr_t pa) 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);