aboutsummaryrefslogtreecommitdiff
path: root/lib/libkvm
diff options
context:
space:
mode:
authorD Scott Phillips <scottph@FreeBSD.org>2020-09-21 22:21:59 +0000
committerD Scott Phillips <scottph@FreeBSD.org>2020-09-21 22:21:59 +0000
commit00e6614750deca2457373aecd5bfa7aebbde3865 (patch)
tree73c950caf7bef81919555e2270ded794535c1b37 /lib/libkvm
parentab041f713aeccdf23b4805ffb71815c8d4aa9c88 (diff)
downloadsrc-00e6614750deca2457373aecd5bfa7aebbde3865.tar.gz
src-00e6614750deca2457373aecd5bfa7aebbde3865.zip
Sparsify the vm_page_dump bitmap
On Ampere Altra systems, the sparse population of RAM within the physical address space causes the vm_page_dump bitmap to be much larger than necessary, increasing the size from ~8 Mib to > 2 Gib (and overflowing `int` for the size). Changing the page dump bitmap also changes the minidump file format, so changes are also necessary in libkvm. Reviewed by: jhb Approved by: scottl (implicit) MFC after: 1 week Sponsored by: Ampere Computing, Inc. Differential Revision: https://reviews.freebsd.org/D26131
Notes
Notes: svn path=/head/; revision=365978
Diffstat (limited to 'lib/libkvm')
-rw-r--r--lib/libkvm/kvm_minidump_aarch64.c20
-rw-r--r--lib/libkvm/kvm_minidump_amd64.c27
-rw-r--r--lib/libkvm/kvm_minidump_arm.c16
-rw-r--r--lib/libkvm/kvm_minidump_i386.c16
-rw-r--r--lib/libkvm/kvm_minidump_mips.c16
-rw-r--r--lib/libkvm/kvm_minidump_powerpc64.c14
-rw-r--r--lib/libkvm/kvm_minidump_riscv.c20
-rw-r--r--lib/libkvm/kvm_private.c86
-rw-r--r--lib/libkvm/kvm_private.h11
9 files changed, 176 insertions, 50 deletions
diff --git a/lib/libkvm/kvm_minidump_aarch64.c b/lib/libkvm/kvm_minidump_aarch64.c
index 48fa877fdc2c..d962a3647b41 100644
--- a/lib/libkvm/kvm_minidump_aarch64.c
+++ b/lib/libkvm/kvm_minidump_aarch64.c
@@ -82,7 +82,7 @@ static int
_aarch64_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
- off_t off, sparse_off;
+ off_t off, dump_avail_off, sparse_off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
if (vmst == NULL) {
@@ -102,7 +102,7 @@ _aarch64_minidump_initvtop(kvm_t *kd)
}
vmst->hdr.version = le32toh(vmst->hdr.version);
- if (vmst->hdr.version != MINIDUMP_VERSION) {
+ 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);
@@ -114,15 +114,21 @@ _aarch64_minidump_initvtop(kvm_t *kd)
vmst->hdr.dmapphys = le64toh(vmst->hdr.dmapphys);
vmst->hdr.dmapbase = le64toh(vmst->hdr.dmapbase);
vmst->hdr.dmapend = le64toh(vmst->hdr.dmapend);
+ vmst->hdr.dumpavailsize = vmst->hdr.version == MINIDUMP_VERSION ?
+ le32toh(vmst->hdr.dumpavailsize) : 0;
/* Skip header and msgbuf */
- off = AARCH64_PAGE_SIZE + aarch64_round_page(vmst->hdr.msgbufsize);
+ dump_avail_off = AARCH64_PAGE_SIZE + aarch64_round_page(vmst->hdr.msgbufsize);
+
+ /* Skip dump_avail */
+ off = dump_avail_off + aarch64_round_page(vmst->hdr.dumpavailsize);
/* build physical address lookup table for sparse pages */
sparse_off = off + aarch64_round_page(vmst->hdr.bitmapsize) +
aarch64_round_page(vmst->hdr.pmapsize);
- if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
- AARCH64_PAGE_SIZE, sizeof(uint64_t)) == -1) {
+ if (_kvm_pt_init(kd, vmst->hdr.dumpavailsize, dump_avail_off,
+ vmst->hdr.bitmapsize, off, sparse_off, AARCH64_PAGE_SIZE,
+ sizeof(uint64_t)) == -1) {
return (-1);
}
off += aarch64_round_page(vmst->hdr.bitmapsize);
@@ -257,7 +263,9 @@ _aarch64_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
}
while (_kvm_bitmap_next(&bm, &bmindex)) {
- pa = bmindex * AARCH64_PAGE_SIZE;
+ pa = _kvm_bit_id_pa(kd, bmindex, AARCH64_PAGE_SIZE);
+ if (pa == _KVM_PA_INVALID)
+ break;
dva = vm->hdr.dmapbase + pa;
if (vm->hdr.dmapend < (dva + AARCH64_PAGE_SIZE))
break;
diff --git a/lib/libkvm/kvm_minidump_amd64.c b/lib/libkvm/kvm_minidump_amd64.c
index 579c6c4b3bae..6d9086bce8c6 100644
--- a/lib/libkvm/kvm_minidump_amd64.c
+++ b/lib/libkvm/kvm_minidump_amd64.c
@@ -123,7 +123,7 @@ static int
_amd64_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
- off_t off, sparse_off;
+ off_t off, dump_avail_off, sparse_off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
if (vmst == NULL) {
@@ -143,10 +143,10 @@ _amd64_minidump_initvtop(kvm_t *kd)
/*
* NB: amd64 minidump header is binary compatible between version 1
- * and version 2; this may not be the case for the future versions.
+ * and version 2; version 3 adds the dumpavailsize field
*/
vmst->hdr.version = le32toh(vmst->hdr.version);
- if (vmst->hdr.version != MINIDUMP_VERSION && vmst->hdr.version != 1) {
+ 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);
@@ -157,14 +157,20 @@ _amd64_minidump_initvtop(kvm_t *kd)
vmst->hdr.kernbase = le64toh(vmst->hdr.kernbase);
vmst->hdr.dmapbase = le64toh(vmst->hdr.dmapbase);
vmst->hdr.dmapend = le64toh(vmst->hdr.dmapend);
+ vmst->hdr.dumpavailsize = vmst->hdr.version == MINIDUMP_VERSION ?
+ le32toh(vmst->hdr.dumpavailsize) : 0;
/* Skip header and msgbuf */
- off = AMD64_PAGE_SIZE + amd64_round_page(vmst->hdr.msgbufsize);
+ dump_avail_off = AMD64_PAGE_SIZE + amd64_round_page(vmst->hdr.msgbufsize);
+
+ /* Skip dump_avail */
+ off = dump_avail_off + amd64_round_page(vmst->hdr.dumpavailsize);
sparse_off = off + amd64_round_page(vmst->hdr.bitmapsize) +
amd64_round_page(vmst->hdr.pmapsize);
- if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
- AMD64_PAGE_SIZE, sizeof(uint64_t)) == -1) {
+ if (_kvm_pt_init(kd, vmst->hdr.dumpavailsize, dump_avail_off,
+ vmst->hdr.bitmapsize, off, sparse_off, AMD64_PAGE_SIZE,
+ sizeof(uint64_t)) == -1) {
return (-1);
}
off += amd64_round_page(vmst->hdr.bitmapsize);
@@ -372,7 +378,7 @@ _amd64_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
pa = (pde & AMD64_PG_PS_FRAME) +
((va & AMD64_PDRMASK) ^ VA_OFF(vm, va));
dva = vm->hdr.dmapbase + pa;
- _kvm_bitmap_set(&bm, pa, AMD64_PAGE_SIZE);
+ _kvm_bitmap_set(&bm, _kvm_pa_bit_id(kd, pa, AMD64_PAGE_SIZE));
if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
_amd64_entry_to_prot(pde), AMD64_NBPDR, pgsz)) {
goto out;
@@ -392,7 +398,8 @@ _amd64_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
pa = pte & AMD64_PG_FRAME;
dva = vm->hdr.dmapbase + pa;
if ((pte & AMD64_PG_V) != 0) {
- _kvm_bitmap_set(&bm, pa, AMD64_PAGE_SIZE);
+ _kvm_bitmap_set(&bm,
+ _kvm_pa_bit_id(kd, pa, AMD64_PAGE_SIZE));
if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
_amd64_entry_to_prot(pte), pgsz, 0)) {
goto out;
@@ -403,7 +410,9 @@ _amd64_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
}
while (_kvm_bitmap_next(&bm, &bmindex)) {
- pa = bmindex * AMD64_PAGE_SIZE;
+ pa = _kvm_bit_id_pa(kd, bmindex, AMD64_PAGE_SIZE);
+ if (pa == _KVM_PA_INVALID)
+ break;
dva = vm->hdr.dmapbase + pa;
if (vm->hdr.dmapend < (dva + pgsz))
break;
diff --git a/lib/libkvm/kvm_minidump_arm.c b/lib/libkvm/kvm_minidump_arm.c
index e515c1c60780..e76889c5eb8f 100644
--- a/lib/libkvm/kvm_minidump_arm.c
+++ b/lib/libkvm/kvm_minidump_arm.c
@@ -86,7 +86,7 @@ static int
_arm_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
- off_t off, sparse_off;
+ off_t off, dump_avail_off, sparse_off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
if (vmst == NULL) {
@@ -108,7 +108,7 @@ _arm_minidump_initvtop(kvm_t *kd)
return (-1);
}
vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version);
- if (vmst->hdr.version != MINIDUMP_VERSION) {
+ 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);
@@ -123,14 +123,20 @@ _arm_minidump_initvtop(kvm_t *kd)
/* This is a safe default as 1K pages are not used. */
vmst->hdr.mmuformat = MINIDUMP_MMU_FORMAT_V6;
}
+ vmst->hdr.dumpavailsize = vmst->hdr.version == MINIDUMP_VERSION ?
+ _kvm32toh(kd, vmst->hdr.dumpavailsize) : 0;
/* Skip header and msgbuf */
- off = ARM_PAGE_SIZE + arm_round_page(vmst->hdr.msgbufsize);
+ dump_avail_off = ARM_PAGE_SIZE + arm_round_page(vmst->hdr.msgbufsize);
+
+ /* Skip dump_avail */
+ off = dump_avail_off + arm_round_page(vmst->hdr.dumpavailsize);
sparse_off = off + arm_round_page(vmst->hdr.bitmapsize) +
arm_round_page(vmst->hdr.ptesize);
- if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
- ARM_PAGE_SIZE, sizeof(uint32_t)) == -1) {
+ if (_kvm_pt_init(kd, vmst->hdr.dumpavailsize, dump_avail_off,
+ vmst->hdr.bitmapsize, off, sparse_off, ARM_PAGE_SIZE,
+ sizeof(uint32_t)) == -1) {
return (-1);
}
off += arm_round_page(vmst->hdr.bitmapsize);
diff --git a/lib/libkvm/kvm_minidump_i386.c b/lib/libkvm/kvm_minidump_i386.c
index 9070da17aa1d..8349c997bdc2 100644
--- a/lib/libkvm/kvm_minidump_i386.c
+++ b/lib/libkvm/kvm_minidump_i386.c
@@ -91,7 +91,7 @@ static int
_i386_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
- off_t off, sparse_off;
+ off_t off, dump_avail_off, sparse_off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
if (vmst == NULL) {
@@ -109,7 +109,7 @@ _i386_minidump_initvtop(kvm_t *kd)
return (-1);
}
vmst->hdr.version = le32toh(vmst->hdr.version);
- if (vmst->hdr.version != MINIDUMP_VERSION) {
+ 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);
@@ -119,14 +119,20 @@ _i386_minidump_initvtop(kvm_t *kd)
vmst->hdr.ptesize = le32toh(vmst->hdr.ptesize);
vmst->hdr.kernbase = le32toh(vmst->hdr.kernbase);
vmst->hdr.paemode = le32toh(vmst->hdr.paemode);
+ vmst->hdr.dumpavailsize = vmst->hdr.version == MINIDUMP_VERSION ?
+ le32toh(vmst->hdr.dumpavailsize) : 0;
/* Skip header and msgbuf */
- off = I386_PAGE_SIZE + i386_round_page(vmst->hdr.msgbufsize);
+ dump_avail_off = I386_PAGE_SIZE + i386_round_page(vmst->hdr.msgbufsize);
+
+ /* Skip dump_avail */
+ off = dump_avail_off + i386_round_page(vmst->hdr.dumpavailsize);
sparse_off = off + i386_round_page(vmst->hdr.bitmapsize) +
i386_round_page(vmst->hdr.ptesize);
- if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
- I386_PAGE_SIZE, sizeof(uint32_t)) == -1) {
+ if (_kvm_pt_init(kd, vmst->hdr.dumpavailsize, dump_avail_off,
+ vmst->hdr.bitmapsize, off, sparse_off, I386_PAGE_SIZE,
+ sizeof(uint32_t)) == -1) {
return (-1);
}
off += i386_round_page(vmst->hdr.bitmapsize);
diff --git a/lib/libkvm/kvm_minidump_mips.c b/lib/libkvm/kvm_minidump_mips.c
index b117213e3bef..4a0774733fc5 100644
--- a/lib/libkvm/kvm_minidump_mips.c
+++ b/lib/libkvm/kvm_minidump_mips.c
@@ -85,7 +85,7 @@ static int
_mips_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
- off_t off, sparse_off;
+ off_t off, dump_avail_off, sparse_off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
if (vmst == NULL) {
@@ -113,7 +113,7 @@ _mips_minidump_initvtop(kvm_t *kd)
return (-1);
}
vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version);
- if (vmst->hdr.version != MINIDUMP_VERSION) {
+ 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);
@@ -124,14 +124,20 @@ _mips_minidump_initvtop(kvm_t *kd)
vmst->hdr.kernbase = _kvm64toh(kd, vmst->hdr.kernbase);
vmst->hdr.dmapbase = _kvm64toh(kd, vmst->hdr.dmapbase);
vmst->hdr.dmapend = _kvm64toh(kd, vmst->hdr.dmapend);
+ vmst->hdr.dumpavailsize = vmst->hdr.version == MINIDUMP_VERSION ?
+ _kvm32toh(kd, vmst->hdr.dumpavailsize) : 0;
/* Skip header and msgbuf */
- off = MIPS_PAGE_SIZE + mips_round_page(vmst->hdr.msgbufsize);
+ dump_avail_off = MIPS_PAGE_SIZE + mips_round_page(vmst->hdr.msgbufsize);
+
+ /* Skip dump_avail */
+ off = dump_avail_off + mips_round_page(vmst->hdr.dumpavailsize);
sparse_off = off + mips_round_page(vmst->hdr.bitmapsize) +
mips_round_page(vmst->hdr.ptesize);
- if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
- MIPS_PAGE_SIZE, sizeof(uint32_t)) == -1) {
+ if (_kvm_pt_init(kd, vmst->hdr.dumpavailsize, dump_avail_off,
+ vmst->hdr.bitmapsize, off, sparse_off, MIPS_PAGE_SIZE,
+ sizeof(uint32_t)) == -1) {
return (-1);
}
off += mips_round_page(vmst->hdr.bitmapsize);
diff --git a/lib/libkvm/kvm_minidump_powerpc64.c b/lib/libkvm/kvm_minidump_powerpc64.c
index 6e95b40aab2a..30ba967a0628 100644
--- a/lib/libkvm/kvm_minidump_powerpc64.c
+++ b/lib/libkvm/kvm_minidump_powerpc64.c
@@ -68,7 +68,7 @@ _powerpc64_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
struct minidumphdr *hdr;
- off_t bitmap_off, pmap_off, sparse_off;
+ off_t dump_avail_off, bitmap_off, pmap_off, sparse_off;
const char *mmu_name;
/* Alloc VM */
@@ -92,7 +92,7 @@ _powerpc64_minidump_initvtop(kvm_t *kd)
}
/* Check version */
hdr->version = be32toh(hdr->version);
- if (hdr->version != MINIDUMP_VERSION) {
+ if (hdr->version != MINIDUMP_VERSION && hdr->version != 1) {
_kvm_err(kd, kd->program, "wrong minidump version. "
"Expected %d got %d", MINIDUMP_VERSION, hdr->version);
goto failed;
@@ -108,6 +108,8 @@ _powerpc64_minidump_initvtop(kvm_t *kd)
hdr->hw_direct_map = be32toh(hdr->hw_direct_map);
hdr->startkernel = be64toh(hdr->startkernel);
hdr->endkernel = be64toh(hdr->endkernel);
+ hdr->dumpavailsize = hdr->version == MINIDUMP_VERSION ?
+ be32toh(hdr->dumpavailsize) : 0;
vmst->kimg_start = PPC64_KERNBASE;
vmst->kimg_end = PPC64_KERNBASE + hdr->endkernel - hdr->startkernel;
@@ -140,7 +142,8 @@ _powerpc64_minidump_initvtop(kvm_t *kd)
goto failed;
/* Get dump parts' offsets */
- bitmap_off = PPC64_PAGE_SIZE + ppc64_round_page(hdr->msgbufsize);
+ dump_avail_off = PPC64_PAGE_SIZE + ppc64_round_page(hdr->msgbufsize);
+ bitmap_off = dump_avail_off + ppc64_round_page(hdr->dumpavailsize);
pmap_off = bitmap_off + ppc64_round_page(hdr->bitmapsize);
sparse_off = pmap_off + ppc64_round_page(hdr->pmapsize);
@@ -151,8 +154,9 @@ _powerpc64_minidump_initvtop(kvm_t *kd)
(uintmax_t)pmap_off, (uintmax_t)sparse_off);
/* build physical address lookup table for sparse pages */
- if (_kvm_pt_init(kd, hdr->bitmapsize, bitmap_off, sparse_off,
- PPC64_PAGE_SIZE, sizeof(uint64_t)) == -1)
+ if (_kvm_pt_init(kd, hdr->dumpavailsize, dump_avail_off,
+ hdr->bitmapsize, bitmap_off, sparse_off, PPC64_PAGE_SIZE,
+ sizeof(uint64_t)) == -1)
goto failed;
if (_kvm_pmap_init(kd, hdr->pmapsize, pmap_off) == -1)
diff --git a/lib/libkvm/kvm_minidump_riscv.c b/lib/libkvm/kvm_minidump_riscv.c
index ffe83b8f99cc..05d713d4b221 100644
--- a/lib/libkvm/kvm_minidump_riscv.c
+++ b/lib/libkvm/kvm_minidump_riscv.c
@@ -83,7 +83,7 @@ static int
_riscv_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
- off_t off, sparse_off;
+ off_t off, dump_avail_off, sparse_off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
if (vmst == NULL) {
@@ -103,7 +103,7 @@ _riscv_minidump_initvtop(kvm_t *kd)
}
vmst->hdr.version = le32toh(vmst->hdr.version);
- if (vmst->hdr.version != MINIDUMP_VERSION) {
+ 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);
@@ -115,15 +115,21 @@ _riscv_minidump_initvtop(kvm_t *kd)
vmst->hdr.dmapphys = le64toh(vmst->hdr.dmapphys);
vmst->hdr.dmapbase = le64toh(vmst->hdr.dmapbase);
vmst->hdr.dmapend = le64toh(vmst->hdr.dmapend);
+ vmst->hdr.dumpavailsize = vmst->hdr.version == MINIDUMP_VERSION ?
+ le32toh(vmst->hdr.dumpavailsize) : 0;
/* Skip header and msgbuf */
- off = RISCV_PAGE_SIZE + riscv_round_page(vmst->hdr.msgbufsize);
+ dump_avail_off = RISCV_PAGE_SIZE + riscv_round_page(vmst->hdr.msgbufsize);
+
+ /* Skip dump_avail */
+ off = dump_avail_off + riscv_round_page(vmst->hdr.dumpavailsize);
/* build physical address lookup table for sparse pages */
sparse_off = off + riscv_round_page(vmst->hdr.bitmapsize) +
riscv_round_page(vmst->hdr.pmapsize);
- if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
- RISCV_PAGE_SIZE, sizeof(uint64_t)) == -1) {
+ if (_kvm_pt_init(kd, vmst->hdr.dumpavailsize, dump_avail_off,
+ vmst->hdr.bitmapsize, off, sparse_off, RISCV_PAGE_SIZE,
+ sizeof(uint64_t)) == -1) {
return (-1);
}
off += riscv_round_page(vmst->hdr.bitmapsize);
@@ -258,7 +264,9 @@ _riscv_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
}
while (_kvm_bitmap_next(&bm, &bmindex)) {
- pa = bmindex * RISCV_PAGE_SIZE;
+ pa = _kvm_bit_id_pa(kd, bmindex, RISCV_PAGE_SIZE);
+ if (pa == _KVM_PA_INVALID)
+ break;
dva = vm->hdr.dmapbase + pa;
if (vm->hdr.dmapend < (dva + RISCV_PAGE_SIZE))
break;
diff --git a/lib/libkvm/kvm_private.c b/lib/libkvm/kvm_private.c
index ea576d8d4d2b..9424502adfad 100644
--- a/lib/libkvm/kvm_private.c
+++ b/lib/libkvm/kvm_private.c
@@ -290,8 +290,9 @@ _kvm_map_get(kvm_t *kd, u_long pa, unsigned int page_size)
}
int
-_kvm_pt_init(kvm_t *kd, size_t map_len, off_t map_off, off_t sparse_off,
- int page_size, int word_size)
+_kvm_pt_init(kvm_t *kd, size_t dump_avail_size, off_t dump_avail_off,
+ size_t map_len, off_t map_off, off_t sparse_off, int page_size,
+ int word_size)
{
uint64_t *addr;
uint32_t *popcount_bin;
@@ -299,6 +300,27 @@ _kvm_pt_init(kvm_t *kd, size_t map_len, off_t map_off, off_t sparse_off,
uint64_t pc_bins, res;
ssize_t rd;
+ kd->dump_avail_size = dump_avail_size;
+ if (dump_avail_size > 0) {
+ kd->dump_avail = mmap(NULL, kd->dump_avail_size, PROT_READ,
+ MAP_PRIVATE, kd->pmfd, dump_avail_off);
+ } else {
+ /*
+ * Older version minidumps don't provide dump_avail[],
+ * so the bitmap is fully populated from 0 to
+ * last_pa. Create an implied dump_avail that
+ * expresses this.
+ */
+ kd->dump_avail = calloc(4, word_size);
+ if (word_size == sizeof(uint32_t)) {
+ ((uint32_t *)kd->dump_avail)[1] = _kvm32toh(kd,
+ map_len * 8 * page_size);
+ } else {
+ kd->dump_avail[1] = _kvm64toh(kd,
+ map_len * 8 * page_size);
+ }
+ }
+
/*
* Map the bitmap specified by the arguments.
*/
@@ -394,6 +416,55 @@ _kvm_pmap_init(kvm_t *kd, uint32_t pmap_size, off_t pmap_off)
return (0);
}
+static inline uint64_t
+dump_avail_n(kvm_t *kd, long i)
+{
+ uint32_t *d32;
+
+ if (kd->pt_word_size == sizeof(uint32_t)) {
+ d32 = (uint32_t *)kd->dump_avail;
+ return (_kvm32toh(kd, d32[i]));
+ } else
+ return (_kvm64toh(kd, kd->dump_avail[i]));
+}
+
+uint64_t
+_kvm_pa_bit_id(kvm_t *kd, uint64_t pa, unsigned int page_size)
+{
+ uint64_t adj;
+ long i;
+
+ adj = 0;
+ for (i = 0; dump_avail_n(kd, i + 1) != 0; i += 2) {
+ if (pa >= dump_avail_n(kd, i + 1)) {
+ adj += howmany(dump_avail_n(kd, i + 1), page_size) -
+ dump_avail_n(kd, i) / page_size;
+ } else {
+ return (pa / page_size -
+ dump_avail_n(kd, i) / page_size + adj);
+ }
+ }
+ return (_KVM_BIT_ID_INVALID);
+}
+
+uint64_t
+_kvm_bit_id_pa(kvm_t *kd, uint64_t bit_id, unsigned int page_size)
+{
+ uint64_t sz;
+ long i;
+
+ for (i = 0; dump_avail_n(kd, i + 1) != 0; i += 2) {
+ sz = howmany(dump_avail_n(kd, i + 1), page_size) -
+ dump_avail_n(kd, i) / page_size;
+ if (bit_id < sz) {
+ return (rounddown2(dump_avail_n(kd, i), page_size) +
+ bit_id * page_size);
+ }
+ bit_id -= sz;
+ }
+ return (_KVM_PA_INVALID);
+}
+
/*
* Find the offset for the given physical page address; returns -1 otherwise.
*
@@ -412,7 +483,7 @@ off_t
_kvm_pt_find(kvm_t *kd, uint64_t pa, unsigned int page_size)
{
uint64_t *bitmap = kd->pt_map;
- uint64_t pte_bit_id = pa / page_size;
+ uint64_t pte_bit_id = _kvm_pa_bit_id(kd, pa, page_size);
uint64_t pte_u64 = pte_bit_id / BITS_IN(*bitmap);
uint64_t popcount_id = pte_bit_id / POPCOUNT_BITS;
uint64_t pte_mask = 1ULL << (pte_bit_id % BITS_IN(*bitmap));
@@ -420,7 +491,8 @@ _kvm_pt_find(kvm_t *kd, uint64_t pa, unsigned int page_size)
uint32_t count;
/* Check whether the page address requested is in the dump. */
- if (pte_bit_id >= (kd->pt_map_size * NBBY) ||
+ if (pte_bit_id == _KVM_BIT_ID_INVALID ||
+ pte_bit_id >= (kd->pt_map_size * NBBY) ||
(bitmap[pte_u64] & pte_mask) == 0)
return (-1);
@@ -714,12 +786,12 @@ _kvm_bitmap_init(struct kvm_bitmap *bm, u_long bitmapsize, u_long *idx)
}
void
-_kvm_bitmap_set(struct kvm_bitmap *bm, u_long pa, unsigned int page_size)
+_kvm_bitmap_set(struct kvm_bitmap *bm, u_long bm_index)
{
- u_long bm_index = pa / page_size;
uint8_t *byte = &bm->map[bm_index / 8];
- *byte |= (1UL << (bm_index % 8));
+ if (bm_index / 8 < bm->size)
+ *byte |= (1UL << (bm_index % 8));
}
int
diff --git a/lib/libkvm/kvm_private.h b/lib/libkvm/kvm_private.h
index e71dde92a2f1..d9018b8f6a5e 100644
--- a/lib/libkvm/kvm_private.h
+++ b/lib/libkvm/kvm_private.h
@@ -106,6 +106,8 @@ struct __kvm {
/* Page table lookup structures. */
uint64_t *pt_map;
size_t pt_map_size;
+ uint64_t *dump_avail; /* actually word sized */
+ size_t dump_avail_size;
off_t pt_sparse_off;
uint64_t pt_sparse_size;
uint32_t *pt_popcounts;
@@ -152,8 +154,13 @@ _kvm64toh(kvm_t *kd, uint64_t val)
return (be64toh(val));
}
+uint64_t _kvm_pa_bit_id(kvm_t *kd, uint64_t pa, unsigned int page_size);
+uint64_t _kvm_bit_id_pa(kvm_t *kd, uint64_t bit_id, unsigned int page_size);
+#define _KVM_PA_INVALID ULONG_MAX
+#define _KVM_BIT_ID_INVALID ULONG_MAX
+
int _kvm_bitmap_init(struct kvm_bitmap *, u_long, u_long *);
-void _kvm_bitmap_set(struct kvm_bitmap *, u_long, unsigned int);
+void _kvm_bitmap_set(struct kvm_bitmap *, u_long);
int _kvm_bitmap_next(struct kvm_bitmap *, u_long *);
void _kvm_bitmap_deinit(struct kvm_bitmap *);
@@ -173,7 +180,7 @@ kvaddr_t _kvm_dpcpu_validaddr(kvm_t *, kvaddr_t);
int _kvm_probe_elf_kernel(kvm_t *, int, int);
int _kvm_is_minidump(kvm_t *);
int _kvm_read_core_phdrs(kvm_t *, size_t *, GElf_Phdr **);
-int _kvm_pt_init(kvm_t *, size_t, off_t, off_t, int, int);
+int _kvm_pt_init(kvm_t *, size_t, off_t, size_t, off_t, off_t, int, int);
off_t _kvm_pt_find(kvm_t *, uint64_t, unsigned int);
int _kvm_visit_cb(kvm_t *, kvm_walk_pages_cb_t *, void *, u_long,
u_long, u_long, vm_prot_t, size_t, unsigned int);