diff options
author | avg <avg@FreeBSD.org> | 2010-11-11 18:35:28 +0000 |
---|---|---|
committer | avg <avg@FreeBSD.org> | 2010-11-11 18:35:28 +0000 |
commit | 6ba2297b754f9d2cd9840610b1327c746071b697 (patch) | |
tree | ab4a8852b8f7a080ba65cb8f77fcec31c195cb3f /lib | |
parent | 37790e770eac68da33354ccc87eaaf139b2a5bb5 (diff) | |
download | FreeBSD-src-6ba2297b754f9d2cd9840610b1327c746071b697.zip FreeBSD-src-6ba2297b754f9d2cd9840610b1327c746071b697.tar.gz |
amd64: introduce minidump version 2
After KVA space was increased to 512GB on amd64 it became impractical
to use PTEs as entries in the minidump map of dumped pages, because size
of that map alone would already be 1GB.
Instead, we now use PDEs as page map entries and employ two stage lookup
in libkvm: virtual address -> PDE -> PTE -> physical address. PTEs are
now dumped as regular pages. Fixed page map size now is 2MB.
libkvm keeps support for accessing amd64 minidumps of version 1.
Support for 1GB pages is added.
Many thanks to Alan Cox for his guidance, numerous reviews, suggestions,
enhancments and corrections.
Reviewed by: alc [kernel part]
MFC after: 15 days
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libkvm/kvm_minidump_amd64.c | 108 |
1 files changed, 94 insertions, 14 deletions
diff --git a/lib/libkvm/kvm_minidump_amd64.c b/lib/libkvm/kvm_minidump_amd64.c index 8f48f98..15630b1 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 *page_map; }; 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->page_map) + free(vm->page_map); free(vm); kd->vmst = NULL; } @@ -156,7 +156,12 @@ _kvm_minidump_initvtop(kvm_t *kd) _kvm_err(kd, kd->program, "not a minidump for this platform"); return (-1); } - if (vmst->hdr.version != MINIDUMP_VERSION) { + + /* + * NB: amd64 minidump header is binary compatible between version 1 + * and version 2; this may not be the case for the future versions. + */ + if (vmst->hdr.version != MINIDUMP_VERSION && vmst->hdr.version != 1) { _kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version); return (-1); @@ -177,17 +182,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->page_map = _kvm_malloc(kd, vmst->hdr.pmapsize); + if (vmst->page_map == NULL) { + _kvm_err(kd, kd->program, "cannot allocate %d bytes for page_map", vmst->hdr.pmapsize); 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->page_map, vmst->hdr.pmapsize, off) != + vmst->hdr.pmapsize) { + _kvm_err(kd, kd->program, "cannot read %d bytes for page_map", vmst->hdr.pmapsize); return (-1); } - off += vmst->hdr.ptesize; + off += vmst->hdr.pmapsize; /* build physical address hash table for sparse pages */ inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off); @@ -196,7 +201,7 @@ _kvm_minidump_initvtop(kvm_t *kd) } static int -_kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa) +_kvm_minidump_vatop_v1(kvm_t *kd, u_long va, off_t *pa) { struct vmstate *vm; u_long offset; @@ -211,7 +216,7 @@ _kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa) if (va >= vm->hdr.kernbase) { pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT; - pte = vm->ptemap[pteindex]; + pte = vm->page_map[pteindex]; if (((u_long)pte & PG_V) == 0) { _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid"); goto invalid; @@ -243,6 +248,78 @@ invalid: return (0); } +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; + 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_MASK; + + if (va >= vm->hdr.kernbase) { + pdeindex = (va - vm->hdr.kernbase) >> PDRSHIFT; + pde = vm->page_map[pdeindex]; + if (((u_long)pde & PG_V) == 0) { + _kvm_err(kd, kd->program, "_kvm_vatop: pde not valid"); + goto invalid; + } + 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); + goto invalid; + } + *pa = ofs + offset; + return (PAGE_SIZE - offset); + } else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) { + a = (va - vm->hdr.dmapbase) & ~PAGE_MASK; + ofs = hpt_find(kd, a); + if (ofs == -1) { + _kvm_err(kd, kd->program, "_kvm_vatop: direct map address 0x%lx not in minidump", va); + goto invalid; + } + *pa = ofs + offset; + return (PAGE_SIZE - offset); + } else { + _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va); + goto invalid; + } + +invalid: + _kvm_err(kd, 0, "invalid address (0x%lx)", va); + return (0); +} + int _kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa) { @@ -251,5 +328,8 @@ _kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa) _kvm_err(kd, 0, "kvm_kvatop called in live kernel!"); return (0); } - return (_kvm_minidump_vatop(kd, va, pa)); + if (((struct vmstate *)kd->vmst)->hdr.version == 1) + return (_kvm_minidump_vatop_v1(kd, va, pa)); + else + return (_kvm_minidump_vatop(kd, va, pa)); } |