summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Andrews <will@FreeBSD.org>2017-11-11 23:30:58 +0000
committerWill Andrews <will@FreeBSD.org>2017-11-11 23:30:58 +0000
commitc9057838bea6ead0dc94d937b5882134ab5435ad (patch)
tree688fb18d3a236699d5ff50ef435dff1d59812d47
parent569aaa3b9707ba1e520c2063653946b58cd5cb7c (diff)
downloadsrc-test2-c9057838bea6ead0dc94d937b5882134ab5435ad.tar.gz
src-test2-c9057838bea6ead0dc94d937b5882134ab5435ad.zip
Notes
-rw-r--r--lib/libkvm/kvm.c15
-rw-r--r--lib/libkvm/kvm.h16
-rw-r--r--lib/libkvm/kvm_aarch64.h6
-rw-r--r--lib/libkvm/kvm_arm.h13
-rw-r--r--lib/libkvm/kvm_i386.h3
-rw-r--r--lib/libkvm/kvm_minidump_aarch64.c98
-rw-r--r--lib/libkvm/kvm_minidump_amd64.c167
-rw-r--r--lib/libkvm/kvm_minidump_arm.c88
-rw-r--r--lib/libkvm/kvm_minidump_i386.c127
-rw-r--r--lib/libkvm/kvm_minidump_mips.c132
-rw-r--r--lib/libkvm/kvm_mips.h25
-rw-r--r--lib/libkvm/kvm_private.c148
-rw-r--r--lib/libkvm/kvm_private.h24
13 files changed, 740 insertions, 122 deletions
diff --git a/lib/libkvm/kvm.c b/lib/libkvm/kvm.c
index a43b6a72a62e..de30165d6ee9 100644
--- a/lib/libkvm/kvm.c
+++ b/lib/libkvm/kvm.c
@@ -49,6 +49,7 @@ static char sccsid[] = "@(#)kvm.c 8.2 (Berkeley) 2/13/94";
#include <sys/linker.h>
#include <sys/pcpu.h>
#include <sys/stat.h>
+#include <sys/mman.h>
#include <net/vnet.h>
@@ -299,6 +300,10 @@ kvm_close(kvm_t *kd)
free((void *)kd->argv);
if (kd->pt_map != NULL)
free(kd->pt_map);
+ if (kd->page_map != NULL)
+ free(kd->page_map);
+ if (kd->sparse_map != MAP_FAILED)
+ munmap(kd->sparse_map, kd->pt_sparse_size);
free((void *)kd);
return (error);
@@ -487,3 +492,13 @@ kvm_native(kvm_t *kd)
return (1);
return (kd->arch->ka_native(kd));
}
+
+int
+kvm_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *closure)
+{
+
+ if (kd->arch->ka_walk_pages == NULL)
+ return (0);
+
+ return (kd->arch->ka_walk_pages(kd, cb, closure));
+}
diff --git a/lib/libkvm/kvm.h b/lib/libkvm/kvm.h
index 77b118cf3c05..5402bf9ae808 100644
--- a/lib/libkvm/kvm.h
+++ b/lib/libkvm/kvm.h
@@ -36,6 +36,7 @@
#include <sys/cdefs.h>
#include <sys/types.h>
#include <nlist.h>
+#include <vm/vm.h>
/* Default version symbol. */
#define VRS_SYM "_version"
@@ -73,7 +74,19 @@ struct kvm_swap {
u_int ksw_reserved2;
};
+struct kvm_page {
+ unsigned int version;
+ u_long paddr;
+ u_long kmap_vaddr;
+ u_long dmap_vaddr;
+ vm_prot_t prot;
+ u_long offset;
+ size_t len;
+ /* end of version 1 */
+};
+
#define SWIF_DEV_PREFIX 0x0002
+#define LIBKVM_WALK_PAGES_VERSION 1
__BEGIN_DECLS
int kvm_close(kvm_t *);
@@ -104,6 +117,9 @@ ssize_t kvm_read(kvm_t *, unsigned long, void *, size_t);
ssize_t kvm_read_zpcpu(kvm_t *, unsigned long, void *, size_t, int);
ssize_t kvm_read2(kvm_t *, kvaddr_t, void *, size_t);
ssize_t kvm_write(kvm_t *, unsigned long, const void *, size_t);
+
+typedef int kvm_walk_pages_cb_t(struct kvm_page *, void *);
+int kvm_walk_pages(kvm_t *, kvm_walk_pages_cb_t *, void *);
__END_DECLS
#endif /* !_KVM_H_ */
diff --git a/lib/libkvm/kvm_aarch64.h b/lib/libkvm/kvm_aarch64.h
index c125d3c109d3..0f977820e3da 100644
--- a/lib/libkvm/kvm_aarch64.h
+++ b/lib/libkvm/kvm_aarch64.h
@@ -40,7 +40,13 @@ typedef uint64_t aarch64_pte_t;
#define AARCH64_PAGE_SIZE (1 << AARCH64_PAGE_SHIFT)
#define AARCH64_PAGE_MASK (AARCH64_PAGE_SIZE - 1)
+/* Source: arm64/include/pte.h */
#define AARCH64_ATTR_MASK 0xfff0000000000fff
+#define AARCH64_ATTR_UXN (1UL << 54)
+#define AARCH64_ATTR_PXN (1UL << 53)
+#define AARCH64_ATTR_XN (AARCH64_ATTR_PXN | AARCH64_ATTR_UXN)
+#define AARCH64_ATTR_AP(x) ((x) << 6)
+#define AARCH64_ATTR_AP_RO (1 << 1)
#define AARCH64_ATTR_DESCR_MASK 3
diff --git a/lib/libkvm/kvm_arm.h b/lib/libkvm/kvm_arm.h
index 38d05cd1441a..035b70e78513 100644
--- a/lib/libkvm/kvm_arm.h
+++ b/lib/libkvm/kvm_arm.h
@@ -53,6 +53,19 @@ typedef uint32_t arm_pt_entry_t;
#define ARM_L2_S_OFFSET (ARM_L2_S_SIZE - 1)
#define ARM_L2_S_FRAME (~ARM_L2_S_OFFSET)
#define ARM_L2_S_SHIFT 12
+#define ARM_L2_TEX1 0x00000080
+#define ARM_PTE2_RO ARM_L2_TEX1
+#define ARM_L2_NX 0x00000001
+#define ARM_PTE2_NX ARM_L2_NX
+
+/*
+ * Note: L2_S_PROT_W differs depending on whether the system is generic or
+ * xscale. This isn't easily accessible in this context, so use an
+ * approximation of 'xscale' which is a subset of 'generic'.
+ */
+#define ARM_L2_AP0(x) ((x) << 4)
+#define ARM_AP_W 0x01
+#define ARM_L2_S_PROT_W (ARM_L2_AP0(ARM_AP_W))
#define ARM_L1_TYPE_INV 0x00 /* Invalid (fault) */
#define ARM_L1_TYPE_C 0x01 /* Coarse L2 */
diff --git a/lib/libkvm/kvm_i386.h b/lib/libkvm/kvm_i386.h
index 51eb6f8f7f70..5440db2adc69 100644
--- a/lib/libkvm/kvm_i386.h
+++ b/lib/libkvm/kvm_i386.h
@@ -53,8 +53,11 @@ typedef uint64_t i386_pde_pae_t;
#define I386_NBPDR_PAE (1 << I386_PDRSHIFT_PAE)
#define I386_PAGE_PS_MASK_PAE (I386_NBPDR_PAE - 1)
+/* Source: i386/include/pmap.h */
#define I386_PG_V 0x001
+#define I386_PG_RW 0x002
#define I386_PG_PS 0x080
+#define I386_PG_NX (1ULL << 63)
#define I386_PG_FRAME_PAE (0x000ffffffffff000ull)
#define I386_PG_PS_FRAME_PAE (0x000fffffffe00000ull)
#define I386_PG_FRAME (0xfffff000)
diff --git a/lib/libkvm/kvm_minidump_aarch64.c b/lib/libkvm/kvm_minidump_aarch64.c
index 7081809faca2..e9c35df23860 100644
--- a/lib/libkvm/kvm_minidump_aarch64.c
+++ b/lib/libkvm/kvm_minidump_aarch64.c
@@ -50,9 +50,16 @@ __FBSDID("$FreeBSD$");
struct vmstate {
struct minidumphdr hdr;
- uint64_t *page_map;
};
+static aarch64_pte_t
+_aarch64_pte_get(kvm_t *kd, u_long pteindex)
+{
+ aarch64_pte_t *pte = _kvm_pmap_get(kd, pteindex, sizeof(*pte));
+
+ return le64toh(*pte);
+}
+
static int
_aarch64_minidump_probe(kvm_t *kd)
{
@@ -66,7 +73,6 @@ _aarch64_minidump_freevtop(kvm_t *kd)
{
struct vmstate *vm = kd->vmst;
- free(vm->page_map);
free(vm);
kd->vmst = NULL;
}
@@ -116,28 +122,11 @@ _aarch64_minidump_initvtop(kvm_t *kd)
aarch64_round_page(vmst->hdr.pmapsize);
if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
AARCH64_PAGE_SIZE, sizeof(uint64_t)) == -1) {
- _kvm_err(kd, kd->program, "cannot load core bitmap");
return (-1);
}
off += aarch64_round_page(vmst->hdr.bitmapsize);
- 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);
- }
- /* This is the end of the dump, savecore may have truncated it. */
- /*
- * XXX: This doesn't make sense. The pmap is not at the end,
- * and if it is truncated we don't have any actual data (it's
- * all stored after the bitmap and pmap. -- jhb
- */
- if (pread(kd->pmfd, vmst->page_map, vmst->hdr.pmapsize, off) <
- AARCH64_PAGE_SIZE) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for page_map",
- vmst->hdr.pmapsize);
+ if (_kvm_pmap_init(kd, vmst->hdr.pmapsize, off) == -1) {
return (-1);
}
off += aarch64_round_page(vmst->hdr.pmapsize);
@@ -161,7 +150,7 @@ _aarch64_minidump_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
a = (va - vm->hdr.dmapbase + vm->hdr.dmapphys) &
~AARCH64_PAGE_MASK;
- ofs = _kvm_pt_find(kd, a);
+ ofs = _kvm_pt_find(kd, a, AARCH64_PAGE_SIZE);
if (ofs == -1) {
_kvm_err(kd, kd->program, "_aarch64_minidump_vatop: "
"direct map address 0x%jx not in minidump",
@@ -172,16 +161,16 @@ _aarch64_minidump_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
return (AARCH64_PAGE_SIZE - offset);
} else if (va >= vm->hdr.kernbase) {
l3_index = (va - vm->hdr.kernbase) >> AARCH64_L3_SHIFT;
- if (l3_index >= vm->hdr.pmapsize / sizeof(*vm->page_map))
+ if (l3_index >= vm->hdr.pmapsize / sizeof(l3))
goto invalid;
- l3 = le64toh(vm->page_map[l3_index]);
+ l3 = _aarch64_pte_get(kd, l3_index);
if ((l3 & AARCH64_ATTR_DESCR_MASK) != AARCH64_L3_PAGE) {
_kvm_err(kd, kd->program,
"_aarch64_minidump_vatop: pde not valid");
goto invalid;
}
a = l3 & ~AARCH64_ATTR_MASK;
- ofs = _kvm_pt_find(kd, a);
+ ofs = _kvm_pt_find(kd, a, AARCH64_PAGE_SIZE);
if (ofs == -1) {
_kvm_err(kd, kd->program, "_aarch64_minidump_vatop: "
"physical address 0x%jx not in minidump",
@@ -225,12 +214,73 @@ _aarch64_native(kvm_t *kd __unused)
#endif
}
+static vm_prot_t
+_aarch64_entry_to_prot(aarch64_pte_t pte)
+{
+ vm_prot_t prot = VM_PROT_READ;
+
+ /* Source: arm64/arm64/pmap.c:pmap_protect() */
+ if ((pte & AARCH64_ATTR_AP(AARCH64_ATTR_AP_RO)) == 0)
+ prot |= VM_PROT_WRITE;
+ if ((pte & AARCH64_ATTR_XN) == 0)
+ prot |= VM_PROT_EXECUTE;
+ return prot;
+}
+
+static int
+_aarch64_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
+{
+ struct vmstate *vm = kd->vmst;
+ u_long nptes = vm->hdr.pmapsize / sizeof(aarch64_pte_t);
+ u_long bmindex, dva, pa, pteindex, va;
+ struct kvm_bitmap bm;
+ vm_prot_t prot;
+ int ret = 0;
+
+ if (!_kvm_bitmap_init(&bm, vm->hdr.bitmapsize, &bmindex))
+ return (0);
+
+ for (pteindex = 0; pteindex < nptes; pteindex++) {
+ aarch64_pte_t pte = _aarch64_pte_get(kd, pteindex);
+
+ if ((pte & AARCH64_ATTR_DESCR_MASK) != AARCH64_L3_PAGE)
+ continue;
+
+ va = vm->hdr.kernbase + (pteindex << AARCH64_L3_SHIFT);
+ pa = pte & ~AARCH64_ATTR_MASK;
+ dva = vm->hdr.dmapbase + pa;
+ if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
+ _aarch64_entry_to_prot(pte), AARCH64_PAGE_SIZE, 0)) {
+ goto out;
+ }
+ }
+
+ while (_kvm_bitmap_next(&bm, &bmindex)) {
+ pa = bmindex * AARCH64_PAGE_SIZE;
+ dva = vm->hdr.dmapbase + pa;
+ if (vm->hdr.dmapend < (dva + AARCH64_PAGE_SIZE))
+ break;
+ va = 0;
+ prot = VM_PROT_READ | VM_PROT_WRITE;
+ if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
+ prot, AARCH64_PAGE_SIZE, 0)) {
+ goto out;
+ }
+ }
+ ret = 1;
+
+out:
+ _kvm_bitmap_deinit(&bm);
+ return (ret);
+}
+
static struct kvm_arch kvm_aarch64_minidump = {
.ka_probe = _aarch64_minidump_probe,
.ka_initvtop = _aarch64_minidump_initvtop,
.ka_freevtop = _aarch64_minidump_freevtop,
.ka_kvatop = _aarch64_minidump_kvatop,
.ka_native = _aarch64_native,
+ .ka_walk_pages = _aarch64_minidump_walk_pages,
};
KVM_ARCH(kvm_aarch64_minidump);
diff --git a/lib/libkvm/kvm_minidump_amd64.c b/lib/libkvm/kvm_minidump_amd64.c
index 1baca96d71f6..dbee980575f1 100644
--- a/lib/libkvm/kvm_minidump_amd64.c
+++ b/lib/libkvm/kvm_minidump_amd64.c
@@ -46,12 +46,59 @@ __FBSDID("$FreeBSD$");
#include "kvm_amd64.h"
#define amd64_round_page(x) roundup2((kvaddr_t)(x), AMD64_PAGE_SIZE)
+#define VM_IS_V1(vm) (vm->hdr.version == 1)
+#define VA_OFF(vm, va) \
+ (VM_IS_V1(vm) ? ((va) & (AMD64_PAGE_SIZE - 1)) : ((va) & AMD64_PAGE_MASK))
struct vmstate {
struct minidumphdr hdr;
- amd64_pte_t *page_map;
};
+static vm_prot_t
+_amd64_entry_to_prot(uint64_t entry)
+{
+ vm_prot_t prot = VM_PROT_READ;
+
+ if ((entry & PG_RW) != 0)
+ prot |= VM_PROT_WRITE;
+ if ((entry & PG_NX) == 0)
+ prot |= VM_PROT_EXECUTE;
+ return prot;
+}
+
+/*
+ * Version 2 minidumps use page directory entries, while version 1 use page
+ * table entries.
+ */
+
+static amd64_pde_t
+_amd64_pde_get(kvm_t *kd, u_long pdeindex)
+{
+ amd64_pde_t *pde = _kvm_pmap_get(kd, pdeindex, sizeof(*pde));
+
+ return le64toh(*pde);
+}
+
+static amd64_pte_t
+_amd64_pte_get(kvm_t *kd, u_long pteindex)
+{
+ amd64_pte_t *pte = _kvm_pmap_get(kd, pteindex, sizeof(*pte));
+
+ return le64toh(*pte);
+}
+
+/* Get the first page table entry for a given page directory index. */
+static amd64_pte_t *
+_amd64_pde_first_pte(kvm_t *kd, u_long pdeindex)
+{
+ u_long *pa;
+
+ pa = _kvm_pmap_get(kd, pdeindex, sizeof(amd64_pde_t));
+ if (pa == NULL)
+ return NULL;
+ return _kvm_map_get(kd, *pa & AMD64_PG_FRAME, AMD64_PAGE_SIZE);
+}
+
static int
_amd64_minidump_probe(kvm_t *kd)
{
@@ -65,7 +112,6 @@ _amd64_minidump_freevtop(kvm_t *kd)
{
struct vmstate *vm = kd->vmst;
- free(vm->page_map);
free(vm);
kd->vmst = NULL;
}
@@ -116,21 +162,11 @@ _amd64_minidump_initvtop(kvm_t *kd)
amd64_round_page(vmst->hdr.pmapsize);
if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
AMD64_PAGE_SIZE, sizeof(uint64_t)) == -1) {
- _kvm_err(kd, kd->program, "cannot load core bitmap");
return (-1);
}
off += amd64_round_page(vmst->hdr.bitmapsize);
- 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->page_map, vmst->hdr.pmapsize, off) !=
- (ssize_t)vmst->hdr.pmapsize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for page_map",
- vmst->hdr.pmapsize);
+ if (_kvm_pmap_init(kd, vmst->hdr.pmapsize, off) == -1) {
return (-1);
}
off += amd64_round_page(vmst->hdr.pmapsize);
@@ -153,16 +189,16 @@ _amd64_minidump_vatop_v1(kvm_t *kd, kvaddr_t va, off_t *pa)
if (va >= vm->hdr.kernbase) {
pteindex = (va - vm->hdr.kernbase) >> AMD64_PAGE_SHIFT;
- if (pteindex >= vm->hdr.pmapsize / sizeof(*vm->page_map))
+ if (pteindex >= vm->hdr.pmapsize / sizeof(pte))
goto invalid;
- pte = le64toh(vm->page_map[pteindex]);
+ pte = _amd64_pte_get(kd, pteindex);
if ((pte & AMD64_PG_V) == 0) {
_kvm_err(kd, kd->program,
"_amd64_minidump_vatop_v1: pte not valid");
goto invalid;
}
a = pte & AMD64_PG_FRAME;
- ofs = _kvm_pt_find(kd, a);
+ ofs = _kvm_pt_find(kd, a, AMD64_PAGE_SIZE);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_amd64_minidump_vatop_v1: physical address 0x%jx not in minidump",
@@ -173,7 +209,7 @@ _amd64_minidump_vatop_v1(kvm_t *kd, kvaddr_t va, off_t *pa)
return (AMD64_PAGE_SIZE - offset);
} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
a = (va - vm->hdr.dmapbase) & ~AMD64_PAGE_MASK;
- ofs = _kvm_pt_find(kd, a);
+ ofs = _kvm_pt_find(kd, a, AMD64_PAGE_SIZE);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_amd64_minidump_vatop_v1: direct map address 0x%jx not in minidump",
@@ -212,9 +248,9 @@ _amd64_minidump_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
if (va >= vm->hdr.kernbase) {
pdeindex = (va - vm->hdr.kernbase) >> AMD64_PDRSHIFT;
- if (pdeindex >= vm->hdr.pmapsize / sizeof(*vm->page_map))
+ if (pdeindex >= vm->hdr.pmapsize / sizeof(pde))
goto invalid;
- pde = le64toh(vm->page_map[pdeindex]);
+ pde = _amd64_pde_get(kd, pdeindex);
if ((pde & AMD64_PG_V) == 0) {
_kvm_err(kd, kd->program,
"_amd64_minidump_vatop: pde not valid");
@@ -223,7 +259,7 @@ _amd64_minidump_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
if ((pde & AMD64_PG_PS) == 0) {
a = pde & AMD64_PG_FRAME;
/* TODO: Just read the single PTE */
- ofs = _kvm_pt_find(kd, a);
+ ofs = _kvm_pt_find(kd, a, AMD64_PAGE_SIZE);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"cannot find page table entry for %ju",
@@ -250,7 +286,7 @@ _amd64_minidump_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
a = pde & AMD64_PG_PS_FRAME;
a += (va & AMD64_PDRMASK) ^ offset;
}
- ofs = _kvm_pt_find(kd, a);
+ ofs = _kvm_pt_find(kd, a, AMD64_PAGE_SIZE);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_amd64_minidump_vatop: physical address 0x%jx not in minidump",
@@ -261,7 +297,7 @@ _amd64_minidump_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
return (AMD64_PAGE_SIZE - offset);
} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
a = (va - vm->hdr.dmapbase) & ~AMD64_PAGE_MASK;
- ofs = _kvm_pt_find(kd, a);
+ ofs = _kvm_pt_find(kd, a, AMD64_PAGE_SIZE);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_amd64_minidump_vatop: direct map address 0x%jx not in minidump",
@@ -297,12 +333,99 @@ _amd64_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
return (_amd64_minidump_vatop(kd, va, pa));
}
+static int
+_amd64_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
+{
+ struct vmstate *vm = kd->vmst;
+ u_long npdes = vm->hdr.pmapsize / sizeof(amd64_pde_t);
+ u_long bmindex, dva, pa, pdeindex, va;
+ struct kvm_bitmap bm;
+ int ret = 0;
+ vm_prot_t prot;
+ unsigned int pgsz = AMD64_PAGE_SIZE;
+
+ if (vm->hdr.version < 2)
+ return (0);
+
+ if (!_kvm_bitmap_init(&bm, vm->hdr.bitmapsize, &bmindex))
+ return (0);
+
+ for (pdeindex = 0; pdeindex < npdes; pdeindex++) {
+ pd_entry_t pde = _amd64_pde_get(kd, pdeindex);
+ pt_entry_t *ptes;
+ u_long i;
+
+ va = vm->hdr.kernbase + (pdeindex << AMD64_PDRSHIFT);
+ if ((pde & PG_V) == 0)
+ continue;
+
+ if ((pde & AMD64_PG_PS) != 0) {
+ /*
+ * Large page. Iterate on each 4K page section
+ * within this page. This differs from 4K pages in
+ * that every page here uses the same PDE to
+ * generate permissions.
+ */
+ 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);
+ if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
+ _amd64_entry_to_prot(pde), AMD64_NBPDR, pgsz)) {
+ goto out;
+ }
+ continue;
+ }
+
+ /* 4K pages: pde references another page of entries. */
+ ptes = _amd64_pde_first_pte(kd, pdeindex);
+ /* Ignore page directory pages that were not dumped. */
+ if (ptes == NULL)
+ continue;
+
+ for (i = 0; i < NPTEPG; i++) {
+ pt_entry_t pte = (u_long)ptes[i];
+
+ pa = pte & AMD64_PG_FRAME;
+ dva = vm->hdr.dmapbase + pa;
+ if ((pte & PG_V) != 0) {
+ _kvm_bitmap_set(&bm, pa, AMD64_PAGE_SIZE);
+ if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
+ _amd64_entry_to_prot(pte), pgsz, 0)) {
+ goto out;
+ }
+ }
+ va += AMD64_PAGE_SIZE;
+ }
+ }
+
+ while (_kvm_bitmap_next(&bm, &bmindex)) {
+ pa = bmindex * AMD64_PAGE_SIZE;
+ dva = vm->hdr.dmapbase + pa;
+ if (vm->hdr.dmapend < (dva + pgsz))
+ break;
+ va = 0;
+ /* amd64/pmap.c: create_pagetables(): dmap always R|W. */
+ prot = VM_PROT_READ | VM_PROT_WRITE;
+ if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva, prot, pgsz, 0)) {
+ goto out;
+ }
+ }
+
+ ret = 1;
+
+out:
+ _kvm_bitmap_deinit(&bm);
+ return (ret);
+}
+
static struct kvm_arch kvm_amd64_minidump = {
.ka_probe = _amd64_minidump_probe,
.ka_initvtop = _amd64_minidump_initvtop,
.ka_freevtop = _amd64_minidump_freevtop,
.ka_kvatop = _amd64_minidump_kvatop,
.ka_native = _amd64_native,
+ .ka_walk_pages = _amd64_minidump_walk_pages,
};
KVM_ARCH(kvm_amd64_minidump);
diff --git a/lib/libkvm/kvm_minidump_arm.c b/lib/libkvm/kvm_minidump_arm.c
index 41b99505f86d..f3f9879f3a19 100644
--- a/lib/libkvm/kvm_minidump_arm.c
+++ b/lib/libkvm/kvm_minidump_arm.c
@@ -51,10 +51,17 @@ __FBSDID("$FreeBSD$");
struct vmstate {
struct minidumphdr hdr;
- void *ptemap;
unsigned char ei_data;
};
+static arm_pt_entry_t
+_arm_pte_get(kvm_t *kd, u_long pteindex)
+{
+ arm_pt_entry_t *pte = _kvm_pmap_get(kd, pteindex, sizeof(*pte));
+
+ return _kvm32toh(kd, *pte);
+}
+
static int
_arm_minidump_probe(kvm_t *kd)
{
@@ -68,7 +75,6 @@ _arm_minidump_freevtop(kvm_t *kd)
{
struct vmstate *vm = kd->vmst;
- free(vm->ptemap);
free(vm);
kd->vmst = NULL;
}
@@ -122,22 +128,11 @@ _arm_minidump_initvtop(kvm_t *kd)
arm_round_page(vmst->hdr.ptesize);
if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
ARM_PAGE_SIZE, sizeof(uint32_t)) == -1) {
- _kvm_err(kd, kd->program, "cannot load core bitmap");
return (-1);
}
off += arm_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);
- return (-1);
- }
-
- if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
- (ssize_t)vmst->hdr.ptesize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
- vmst->hdr.ptesize);
+ if (_kvm_pmap_init(kd, vmst->hdr.ptesize, off) == -1) {
return (-1);
}
off += arm_round_page(vmst->hdr.ptesize);
@@ -153,7 +148,6 @@ _arm_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
arm_physaddr_t offset, a;
kvaddr_t pteindex;
off_t ofs;
- arm_pt_entry_t *ptemap;
if (ISALIVE(kd)) {
_kvm_err(kd, 0, "_arm_minidump_kvatop called in live kernel!");
@@ -161,13 +155,12 @@ _arm_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
}
vm = kd->vmst;
- ptemap = vm->ptemap;
if (va >= vm->hdr.kernbase) {
pteindex = (va - vm->hdr.kernbase) >> ARM_PAGE_SHIFT;
- if (pteindex >= vm->hdr.ptesize / sizeof(*ptemap))
+ if (pteindex >= vm->hdr.ptesize / sizeof(pte))
goto invalid;
- pte = _kvm32toh(kd, ptemap[pteindex]);
+ pte = _arm_pte_get(kd, pteindex);
if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_INV) {
_kvm_err(kd, kd->program,
"_arm_minidump_kvatop: pte not valid");
@@ -190,7 +183,7 @@ _arm_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
a = pte & ARM_L2_S_FRAME;
}
- ofs = _kvm_pt_find(kd, a);
+ ofs = _kvm_pt_find(kd, a, ARM_PAGE_SIZE);
if (ofs == -1) {
_kvm_err(kd, kd->program, "_arm_minidump_kvatop: "
"physical address 0x%jx not in minidump",
@@ -209,12 +202,69 @@ invalid:
return (0);
}
+static vm_prot_t
+_arm_entry_to_prot(kvm_t *kd, arm_pt_entry_t pte)
+{
+ struct vmstate *vm = kd->vmst;
+ vm_prot_t prot = VM_PROT_READ;
+
+ /* Source: arm/arm/pmap-v4.c:pmap_fault_fixup() */
+ if (vm->hdr.mmuformat == MINIDUMP_MMU_FORMAT_V4) {
+ if (pte & ARM_L2_S_PROT_W)
+ prot |= VM_PROT_WRITE;
+ return prot;
+ }
+
+ /* Source: arm/arm/pmap-v6.c:pmap_protect() */
+ if ((pte & ARM_PTE2_RO) == 0)
+ prot |= VM_PROT_WRITE;
+ if ((pte & ARM_PTE2_NX) == 0)
+ prot |= VM_PROT_EXECUTE;
+ return prot;
+}
+
+static int
+_arm_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
+{
+ struct vmstate *vm = kd->vmst;
+ u_long nptes = vm->hdr.ptesize / sizeof(arm_pt_entry_t);
+ u_long dva, pa, pteindex, va;
+
+ for (pteindex = 0; pteindex < nptes; pteindex++) {
+ arm_pt_entry_t pte = _arm_pte_get(kd, pteindex);
+
+ if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_INV)
+ continue;
+
+ va = vm->hdr.kernbase + (pteindex << ARM_PAGE_SHIFT);
+ if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_L) {
+ /* 64K page */
+ pa = (pte & ARM_L2_L_FRAME) +
+ (va & ARM_L2_L_OFFSET & ARM_L2_S_FRAME);
+ } else {
+ if (vm->hdr.mmuformat == MINIDUMP_MMU_FORMAT_V4 &&
+ (pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_T) {
+ continue;
+ }
+ /* 4K page */
+ pa = pte & ARM_L2_S_FRAME;
+ }
+
+ dva = 0; /* no direct map on this platform */
+ if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
+ _arm_entry_to_prot(kd, pte), ARM_PAGE_SIZE, 0))
+ return (0);
+ }
+ return (1);
+}
+
static struct kvm_arch kvm_arm_minidump = {
.ka_probe = _arm_minidump_probe,
.ka_initvtop = _arm_minidump_initvtop,
.ka_freevtop = _arm_minidump_freevtop,
.ka_kvatop = _arm_minidump_kvatop,
.ka_native = _arm_native,
+ .ka_walk_pages = _arm_minidump_walk_pages,
};
KVM_ARCH(kvm_arm_minidump);
diff --git a/lib/libkvm/kvm_minidump_i386.c b/lib/libkvm/kvm_minidump_i386.c
index 129a14b19d72..3a31890d72cf 100644
--- a/lib/libkvm/kvm_minidump_i386.c
+++ b/lib/libkvm/kvm_minidump_i386.c
@@ -49,9 +49,24 @@ __FBSDID("$FreeBSD$");
struct vmstate {
struct minidumphdr hdr;
- void *ptemap;
};
+static i386_pte_pae_t
+_i386_pte_pae_get(kvm_t *kd, u_long pteindex)
+{
+ i386_pte_pae_t *pte = _kvm_pmap_get(kd, pteindex, sizeof(*pte));
+
+ return le64toh(*pte);
+}
+
+static i386_pte_t
+_i386_pte_get(kvm_t *kd, u_long pteindex)
+{
+ i386_pte_t *pte = _kvm_pmap_get(kd, pteindex, sizeof(*pte));
+
+ return le32toh(*pte);
+}
+
static int
_i386_minidump_probe(kvm_t *kd)
{
@@ -65,7 +80,6 @@ _i386_minidump_freevtop(kvm_t *kd)
{
struct vmstate *vm = kd->vmst;
- free(vm->ptemap);
free(vm);
kd->vmst = NULL;
}
@@ -110,19 +124,11 @@ _i386_minidump_initvtop(kvm_t *kd)
i386_round_page(vmst->hdr.ptesize);
if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
I386_PAGE_SIZE, sizeof(uint32_t)) == -1) {
- _kvm_err(kd, kd->program, "cannot load core bitmap");
return (-1);
}
off += i386_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);
- return (-1);
- }
- if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
- (ssize_t)vmst->hdr.ptesize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize);
+ if (_kvm_pmap_init(kd, vmst->hdr.ptesize, off) == -1) {
return (-1);
}
off += i386_round_page(vmst->hdr.ptesize);
@@ -139,24 +145,22 @@ _i386_minidump_vatop_pae(kvm_t *kd, kvaddr_t va, off_t *pa)
kvaddr_t pteindex;
i386_physaddr_pae_t a;
off_t ofs;
- i386_pte_pae_t *ptemap;
vm = kd->vmst;
- ptemap = vm->ptemap;
offset = va & I386_PAGE_MASK;
if (va >= vm->hdr.kernbase) {
pteindex = (va - vm->hdr.kernbase) >> I386_PAGE_SHIFT;
- if (pteindex >= vm->hdr.ptesize / sizeof(*ptemap))
+ if (pteindex >= vm->hdr.ptesize / sizeof(pte))
goto invalid;
- pte = le64toh(ptemap[pteindex]);
+ pte = _i386_pte_pae_get(kd, pteindex);
if ((pte & I386_PG_V) == 0) {
_kvm_err(kd, kd->program,
"_i386_minidump_vatop_pae: pte not valid");
goto invalid;
}
a = pte & I386_PG_FRAME_PAE;
- ofs = _kvm_pt_find(kd, a);
+ ofs = _kvm_pt_find(kd, a, I386_PAGE_SIZE);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_i386_minidump_vatop_pae: physical address 0x%jx not in minidump",
@@ -186,24 +190,22 @@ _i386_minidump_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
kvaddr_t pteindex;
i386_physaddr_t a;
off_t ofs;
- i386_pte_t *ptemap;
vm = kd->vmst;
- ptemap = vm->ptemap;
offset = va & I386_PAGE_MASK;
if (va >= vm->hdr.kernbase) {
pteindex = (va - vm->hdr.kernbase) >> I386_PAGE_SHIFT;
- if (pteindex >= vm->hdr.ptesize / sizeof(*ptemap))
+ if (pteindex >= vm->hdr.ptesize / sizeof(pte))
goto invalid;
- pte = le32toh(ptemap[pteindex]);
+ pte = _i386_pte_get(kd, pteindex);
if ((pte & I386_PG_V) == 0) {
_kvm_err(kd, kd->program,
"_i386_minidump_vatop: pte not valid");
goto invalid;
}
a = pte & I386_PG_FRAME;
- ofs = _kvm_pt_find(kd, a);
+ ofs = _kvm_pt_find(kd, a, I386_PAGE_SIZE);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_i386_minidump_vatop: physical address 0x%jx not in minidump",
@@ -238,12 +240,95 @@ _i386_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
return (_i386_minidump_vatop(kd, va, pa));
}
+static vm_prot_t
+_i386_entry_to_prot(uint64_t pte)
+{
+ vm_prot_t prot = VM_PROT_READ;
+
+ /* Source: i386/pmap.c:pmap_protect() */
+ if (pte & I386_PG_RW)
+ prot |= VM_PROT_WRITE;
+ if ((pte & I386_PG_NX) == 0)
+ prot |= VM_PROT_EXECUTE;
+
+ return prot;
+}
+
+struct i386_iter {
+ kvm_t *kd;
+ u_long nptes;
+ u_long pteindex;
+};
+
+static void
+_i386_iterator_init(struct i386_iter *it, kvm_t *kd)
+{
+ struct vmstate *vm = kd->vmst;
+
+ it->kd = kd;
+ it->pteindex = 0;
+ if (vm->hdr.paemode) {
+ it->nptes = vm->hdr.ptesize / sizeof(i386_pte_pae_t);
+ } else {
+ it->nptes = vm->hdr.ptesize / sizeof(i386_pte_t);
+ }
+ return;
+}
+
+static int
+_i386_iterator_next(struct i386_iter *it, u_long *pa, u_long *va, u_long *dva,
+ vm_prot_t *prot)
+{
+ struct vmstate *vm = it->kd->vmst;
+ i386_pte_t pte32;
+ i386_pte_pae_t pte64;
+ int found = 0;
+
+ *dva = 0;
+ for (; it->pteindex < it->nptes && found == 0; it->pteindex++) {
+ if (vm->hdr.paemode) {
+ pte64 = _i386_pte_pae_get(it->kd, it->pteindex);
+ if ((pte64 & I386_PG_V) == 0)
+ continue;
+ *prot = _i386_entry_to_prot(pte64);
+ *pa = pte64 & I386_PG_FRAME_PAE;
+ } else {
+ pte32 = _i386_pte_get(it->kd, it->pteindex);
+ if ((pte32 & I386_PG_V) == 0)
+ continue;
+ *prot = _i386_entry_to_prot(pte32);
+ *pa = pte32 & I386_PG_FRAME;
+ }
+ *va = vm->hdr.kernbase + (it->pteindex << I386_PAGE_SHIFT);
+ found = 1;
+ }
+ return found;
+}
+
+static int
+_i386_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
+{
+ struct i386_iter it;
+ u_long dva, pa, va;
+ vm_prot_t prot;
+
+ _i386_iterator_init(&it, kd);
+ while (_i386_iterator_next(&it, &pa, &va, &dva, &prot)) {
+ if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
+ prot, I386_PAGE_SIZE, 0)) {
+ return (0);
+ }
+ }
+ return (1);
+}
+
static struct kvm_arch kvm_i386_minidump = {
.ka_probe = _i386_minidump_probe,
.ka_initvtop = _i386_minidump_initvtop,
.ka_freevtop = _i386_minidump_freevtop,
.ka_kvatop = _i386_minidump_kvatop,
.ka_native = _i386_native,
+ .ka_walk_pages = _i386_minidump_walk_pages,
};
KVM_ARCH(kvm_i386_minidump);
diff --git a/lib/libkvm/kvm_minidump_mips.c b/lib/libkvm/kvm_minidump_mips.c
index 96534e8d04f9..b0594490ff40 100644
--- a/lib/libkvm/kvm_minidump_mips.c
+++ b/lib/libkvm/kvm_minidump_mips.c
@@ -52,7 +52,6 @@ __FBSDID("$FreeBSD$");
struct vmstate {
struct minidumphdr hdr;
- void *ptemap;
int pte_size;
};
@@ -73,7 +72,6 @@ _mips_minidump_freevtop(kvm_t *kd)
{
struct vmstate *vm = kd->vmst;
- free(vm->ptemap);
free(vm);
kd->vmst = NULL;
}
@@ -129,22 +127,11 @@ _mips_minidump_initvtop(kvm_t *kd)
mips_round_page(vmst->hdr.ptesize);
if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
MIPS_PAGE_SIZE, sizeof(uint32_t)) == -1) {
- _kvm_err(kd, kd->program, "cannot load core bitmap");
return (-1);
}
off += mips_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);
- return (-1);
- }
-
- if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
- (ssize_t)vmst->hdr.ptesize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
- vmst->hdr.ptesize);
+ if (_kvm_pmap_init(kd, vmst->hdr.ptesize, off) == -1) {
return (-1);
}
off += mips_round_page(vmst->hdr.ptesize);
@@ -156,12 +143,12 @@ static int
_mips_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
{
struct vmstate *vm;
- uint64_t pte;
mips_physaddr_t offset, a;
kvaddr_t pteindex;
+ u_long valid;
off_t ofs;
- uint32_t *ptemap32;
- uint64_t *ptemap64;
+ mips32_pte_t pte32;
+ mips64_pte_t pte64;
if (ISALIVE(kd)) {
_kvm_err(kd, 0, "_mips_minidump_kvatop called in live kernel!");
@@ -173,9 +160,6 @@ _mips_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
va &= ~MIPS_PAGE_MASK;
vm = kd->vmst;
- ptemap32 = vm->ptemap;
- ptemap64 = vm->ptemap;
-
if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64) {
if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END) {
a = va & MIPS_XKPHYS_PHYS_MASK;
@@ -202,17 +186,22 @@ _mips_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
if (va >= vm->hdr.kernbase) {
pteindex = (va - vm->hdr.kernbase) >> MIPS_PAGE_SHIFT;
if (vm->pte_size == 64) {
- if (pteindex >= vm->hdr.ptesize / sizeof(*ptemap64))
+ valid = pteindex < vm->hdr.ptesize / sizeof(pte64);
+ if (pteindex >= vm->hdr.ptesize / sizeof(pte64))
goto invalid;
- pte = _kvm64toh(kd, ptemap64[pteindex]);
- a = MIPS64_PTE_TO_PA(pte);
+ pte64 = _mips64_pte_get(kd, pteindex);
+ valid = pte64 & MIPS_PTE_V;
+ if (valid)
+ a = MIPS64_PTE_TO_PA(pte64);
} else {
- if (pteindex >= vm->hdr.ptesize / sizeof(*ptemap32))
+ if (pteindex >= vm->hdr.ptesize / sizeof(pte32))
goto invalid;
- pte = _kvm32toh(kd, ptemap32[pteindex]);
- a = MIPS32_PTE_TO_PA(pte);
+ pte32 = _mips32_pte_get(kd, pteindex);
+ valid = pte32 & MIPS_PTE_V;
+ if (valid)
+ a = MIPS32_PTE_TO_PA(pte32);
}
- if (!pte) {
+ if (!valid) {
_kvm_err(kd, kd->program, "_mips_minidump_kvatop: pte "
"not valid");
goto invalid;
@@ -224,7 +213,7 @@ _mips_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
}
found:
- ofs = _kvm_pt_find(kd, a);
+ ofs = _kvm_pt_find(kd, a, MIPS_PAGE_SIZE);
if (ofs == -1) {
_kvm_err(kd, kd->program, "_mips_minidump_kvatop: physical "
"address 0x%jx not in minidump", (uintmax_t)a);
@@ -273,12 +262,99 @@ _mips_native(kvm_t *kd __unused)
#endif
}
+struct mips_iter {
+ kvm_t *kd;
+ u_long nptes;
+ u_long pteindex;
+};
+
+static void
+_mips_iterator_init(struct mips_iter *it, kvm_t *kd)
+{
+ struct vmstate *vm = kd->vmst;
+
+ it->kd = kd;
+ it->pteindex = 0;
+ if (vm->pte_size == 64)
+ it->nptes = vm->hdr.ptesize / sizeof(mips64_pte_t);
+ else
+ it->nptes = vm->hdr.ptesize / sizeof(mips32_pte_t);
+ return;
+}
+
+static int
+_mips_iterator_next(struct mips_iter *it, u_long *pa, u_long *va, u_long *dva,
+ vm_prot_t *prot)
+{
+ struct vmstate *vm = it->kd->vmst;
+ int found = 0;
+ mips64_pte_t pte64;
+ mips32_pte_t pte32;
+
+ /*
+ * mips/mips/pmap.c: init_pte_prot / pmap_protect indicate that all
+ * pages are R|X at least.
+ */
+ *prot = VM_PROT_READ | VM_PROT_EXECUTE;
+ *dva = 0;
+ for (;it->pteindex < it->nptes && found == 0; it->pteindex++) {
+ if (vm->pte_size == 64) {
+ pte64 = _mips64_pte_get(it->kd, it->pteindex);
+ if ((pte64 & MIPS_PTE_V) == 0)
+ continue;
+ if ((pte64 & MIPS64_PTE_RO) == 0)
+ *prot |= VM_PROT_WRITE;
+ *pa = MIPS64_PTE_TO_PA(pte64);
+ } else {
+ pte32 = _mips32_pte_get(it->kd, it->pteindex);
+ if ((pte32 & MIPS_PTE_V) == 0)
+ continue;
+ if ((pte32 & MIPS32_PTE_RO) == 0)
+ *prot |= VM_PROT_WRITE;
+ *pa = MIPS32_PTE_TO_PA(pte32);
+ }
+ *va = vm->hdr.kernbase + (it->pteindex << MIPS_PAGE_SHIFT);
+ found = 1;
+ /* advance pteindex regardless */
+ }
+
+ return found;
+}
+
+static int
+_mips_minidump_walk_pages(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg)
+{
+ struct mips_iter it;
+ u_long dva, pa, va;
+ vm_prot_t prot;
+
+ /* Generate direct mapped entries; need page entries for prot etc? */
+ if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64) {
+ /* MIPS_XKPHYS_START..MIPS_XKPHYS_END */
+ /* MIPS64_KSEG0_START..MIPS64_KSEG0_END */
+ /* MIPS64_KSEG1_START..MIPS64_KSEG1_START */
+ } else {
+ /* MIPS32_KSEG0_START..MIPS32_KSEG0_END */
+ /* MIPS32_KSEG1_START..MIPS32_KSEG1_END */
+ }
+
+ _mips_iterator_init(&it, kd);
+ while (_mips_iterator_next(&it, &pa, &va, &dva, &prot)) {
+ if (!_kvm_visit_cb(kd, cb, arg, pa, va, dva,
+ prot, MIPS_PAGE_SIZE, 0)) {
+ return (0);
+ }
+ }
+ return (1);
+}
+
static struct kvm_arch kvm_mips_minidump = {
.ka_probe = _mips_minidump_probe,
.ka_initvtop = _mips_minidump_initvtop,
.ka_freevtop = _mips_minidump_freevtop,
.ka_kvatop = _mips_minidump_kvatop,
.ka_native = _mips_native,
+ .ka_walk_pages = _mips_minidump_walk_pages,
};
KVM_ARCH(kvm_mips_minidump);
diff --git a/lib/libkvm/kvm_mips.h b/lib/libkvm/kvm_mips.h
index 9fba6fb8e6e8..715117d59fb9 100644
--- a/lib/libkvm/kvm_mips.h
+++ b/lib/libkvm/kvm_mips.h
@@ -35,6 +35,9 @@
typedef uint64_t mips_physaddr_t;
+typedef uint32_t mips32_pte_t;
+typedef uint64_t mips64_pte_t;
+
#define MIPS_PAGE_SHIFT 12
#define MIPS_PAGE_SIZE (1 << MIPS_PAGE_SHIFT)
#define MIPS_PAGE_MASK (MIPS_PAGE_SIZE - 1)
@@ -58,6 +61,28 @@ typedef uint64_t mips_physaddr_t;
#define MIPS64_PTE_TO_PFN(pte) ((pte) & MIPS64_PFN_MASK)
#define MIPS64_PTE_TO_PA(pte) (MIPS_PFN_TO_PA(MIPS64_PTE_TO_PFN((pte))))
+#define MIPS32_SWBITS_SHIFT 29
+#define MIPS64_SWBITS_SHIFT 55
+#define MIPS_PTE_V 0x02
+#define MIPS32_PTE_RO ((mips32_pte_t)0x01 << MIPS32_SWBITS_SHIFT)
+#define MIPS64_PTE_RO ((mips64_pte_t)0x01 << MIPS64_SWBITS_SHIFT)
+
+static inline mips32_pte_t
+_mips32_pte_get(kvm_t *kd, u_long pteindex)
+{
+ mips32_pte_t *pte = _kvm_pmap_get(kd, pteindex, sizeof(*pte));
+
+ return _kvm32toh(kd, *pte);
+}
+
+static inline mips64_pte_t
+_mips64_pte_get(kvm_t *kd, u_long pteindex)
+{
+ mips64_pte_t *pte = _kvm_pmap_get(kd, pteindex, sizeof(*pte));
+
+ return _kvm64toh(kd, *pte);
+}
+
#ifdef __mips__
_Static_assert(PAGE_SHIFT == MIPS_PAGE_SHIFT, "PAGE_SHIFT mismatch");
_Static_assert(PAGE_SIZE == MIPS_PAGE_SIZE, "PAGE_SIZE mismatch");
diff --git a/lib/libkvm/kvm_private.c b/lib/libkvm/kvm_private.c
index 4f35fa09bcd2..01611f7d4b15 100644
--- a/lib/libkvm/kvm_private.c
+++ b/lib/libkvm/kvm_private.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <sys/linker.h>
#include <sys/pcpu.h>
#include <sys/stat.h>
+#include <sys/mman.h>
#include <net/vnet.h>
@@ -57,6 +58,7 @@ __FBSDID("$FreeBSD$");
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
+#include <inttypes.h>
#include "kvm_private.h"
@@ -258,6 +260,32 @@ popcount_bytes(uint64_t *addr, uint32_t bit0, uint32_t bitN)
return (count);
}
+void *
+_kvm_pmap_get(kvm_t *kd, u_long index, size_t len)
+{
+ off_t off = index * len;
+
+ if (off >= kd->pt_sparse_off)
+ return (NULL);
+ return (void *)((uintptr_t)kd->page_map + off);
+}
+
+void *
+_kvm_map_get(kvm_t *kd, u_long pa, unsigned int page_size)
+{
+ off_t off;
+ uintptr_t addr;
+
+ off = _kvm_pt_find(kd, pa, page_size);
+ if (off == -1)
+ return NULL;
+
+ addr = (uintptr_t)kd->page_map + off;
+ if (off >= kd->pt_sparse_off)
+ addr = (uintptr_t)kd->sparse_map + (off - kd->pt_sparse_off);
+ return (void *)addr;
+}
+
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)
@@ -300,8 +328,10 @@ _kvm_pt_init(kvm_t *kd, size_t map_len, off_t map_off, off_t sparse_off,
res = map_len;
pc_bins = 1 + (res * NBBY + POPCOUNT_BITS / 2) / POPCOUNT_BITS;
kd->pt_popcounts = calloc(pc_bins, sizeof(uint32_t));
- if (kd->pt_popcounts == NULL)
+ if (kd->pt_popcounts == NULL) {
+ _kvm_err(kd, kd->program, "cannot allocate popcount bins");
return (-1);
+ }
for (popcount_bin = &kd->pt_popcounts[1]; res > 0;
addr++, res -= sizeof(*addr)) {
@@ -318,9 +348,46 @@ _kvm_pt_init(kvm_t *kd, size_t map_len, off_t map_off, off_t sparse_off,
((uintptr_t)popcount_bin - (uintptr_t)kd->pt_popcounts));
kd->pt_sparse_off = sparse_off;
- kd->pt_sparse_size = (uint64_t)*popcount_bin * PAGE_SIZE;
+ kd->pt_sparse_size = (uint64_t)*popcount_bin * page_size;
kd->pt_page_size = page_size;
kd->pt_word_size = word_size;
+
+ /*
+ * Map the sparse page array. This is useful for performing point
+ * lookups of specific pages, e.g. for kvm_walk_pages. Generally,
+ * this is much larger than is reasonable to read in up front, so
+ * mmap it in instead.
+ */
+ kd->sparse_map = mmap(NULL, kd->pt_sparse_size, PROT_READ,
+ MAP_PRIVATE, kd->pmfd, kd->pt_sparse_off);
+ if (kd->sparse_map == MAP_FAILED) {
+ _kvm_err(kd, kd->program, "cannot map %" PRIu64
+ " bytes from fd %d offset %ld for sparse map: %s",
+ kd->pt_sparse_size, kd->pmfd,
+ kd->pt_sparse_off, strerror(errno));
+ return (-1);
+ }
+ return (0);
+}
+
+int
+_kvm_pmap_init(kvm_t *kd, uint32_t pmap_size, off_t pmap_off)
+{
+ ssize_t exp_len = pmap_size;
+
+ kd->page_map_size = pmap_size;
+ kd->page_map_off = pmap_off;
+ kd->page_map = _kvm_malloc(kd, pmap_size);
+ if (kd->page_map == NULL) {
+ _kvm_err(kd, kd->program, "cannot allocate %u bytes "
+ "for page map", pmap_size);
+ return (-1);
+ }
+ if (pread(kd->pmfd, kd->page_map, pmap_size, pmap_off) != exp_len) {
+ _kvm_err(kd, kd->program, "cannot read %d bytes from "
+ "offset %ld for page map", pmap_size, pmap_off);
+ return (-1);
+ }
return (0);
}
@@ -328,7 +395,7 @@ _kvm_pt_init(kvm_t *kd, size_t map_len, off_t map_off, off_t sparse_off,
* Find the offset for the given physical page address; returns -1 otherwise.
*
* A page's offset is represented by the sparse page base offset plus the
- * number of bits set before its bit multiplied by PAGE_SIZE. This means
+ * number of bits set before its bit multiplied by page size. This means
* that if a page exists in the dump, it's necessary to know how many pages
* in the dump precede it. Reduce this O(n) counting to O(1) by caching the
* number of bits set at POPCOUNT_BITS intervals.
@@ -339,10 +406,10 @@ _kvm_pt_init(kvm_t *kd, size_t map_len, off_t map_off, off_t sparse_off,
* checked by also counting down from the next higher bin if it's closer.
*/
off_t
-_kvm_pt_find(kvm_t *kd, uint64_t pa)
+_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 = 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));
@@ -383,10 +450,10 @@ _kvm_pt_find(kvm_t *kd, uint64_t pa)
* This can only happen if the core is truncated. Treat these
* entries as if they don't exist, since their backing doesn't.
*/
- if (count >= (kd->pt_sparse_size / PAGE_SIZE))
+ if (count >= (kd->pt_sparse_size / page_size))
return (-1);
- return (kd->pt_sparse_off + (uint64_t)count * PAGE_SIZE);
+ return (kd->pt_sparse_off + (uint64_t)count * page_size);
}
static int
@@ -630,3 +697,70 @@ again:
_kvm_syserr(kd, kd->program, "kvm_nlist");
return (error);
}
+
+int
+_kvm_bitmap_init(struct kvm_bitmap *bm, u_long bitmapsize, u_long *index)
+{
+
+ *index = ULONG_MAX;
+ bm->map = calloc(bitmapsize, sizeof *bm->map);
+ if (bm->map == NULL)
+ return (0);
+ bm->size = bitmapsize;
+ return (1);
+}
+
+void
+_kvm_bitmap_set(struct kvm_bitmap *bm, u_long pa, unsigned int page_size)
+{
+ u_long bm_index = pa / page_size;
+ uint8_t *byte = &bm->map[bm_index / 8];
+
+ *byte |= (1UL << (bm_index % 8));
+}
+
+int
+_kvm_bitmap_next(struct kvm_bitmap *bm, u_long *index)
+{
+ u_long first_invalid = bm->size * CHAR_BIT;
+
+ if (*index == ULONG_MAX)
+ *index = 0;
+ else
+ (*index)++;
+
+ /* Find the next valid index. */
+ for (; *index < first_invalid; (*index)++) {
+ unsigned int mask = *index % CHAR_BIT;
+ if ((bm->map[*index * CHAR_BIT] & mask) == 0)
+ break;
+ }
+
+ return (*index < first_invalid);
+}
+
+void
+_kvm_bitmap_deinit(struct kvm_bitmap *bm)
+{
+
+ free(bm->map);
+}
+
+int
+_kvm_visit_cb(kvm_t *kd, kvm_walk_pages_cb_t *cb, void *arg, u_long pa,
+ u_long kmap_vaddr, u_long dmap_vaddr, vm_prot_t prot, size_t len,
+ unsigned int page_size)
+{
+ unsigned int pgsz = page_size ? page_size : len;
+ struct kvm_page p = {
+ .version = LIBKVM_WALK_PAGES_VERSION,
+ .paddr = pa,
+ .kmap_vaddr = kmap_vaddr,
+ .dmap_vaddr = dmap_vaddr,
+ .prot = prot,
+ .offset = _kvm_pt_find(kd, pa, pgsz),
+ .len = len,
+ };
+
+ return cb(&p, arg);
+}
diff --git a/lib/libkvm/kvm_private.h b/lib/libkvm/kvm_private.h
index 03ae4cefb815..03c7ee45a620 100644
--- a/lib/libkvm/kvm_private.h
+++ b/lib/libkvm/kvm_private.h
@@ -44,6 +44,7 @@ struct kvm_arch {
void (*ka_freevtop)(kvm_t *);
int (*ka_kvatop)(kvm_t *, kvaddr_t, off_t *);
int (*ka_native)(kvm_t *);
+ int (*ka_walk_pages)(kvm_t *, kvm_walk_pages_cb_t *, void *);
};
#define KVM_ARCH(ka) DATA_SET(kvm_arch, ka)
@@ -107,6 +108,17 @@ struct __kvm {
uint32_t *pt_popcounts;
unsigned int pt_page_size;
unsigned int pt_word_size;
+
+ /* Page & sparse map structures. */
+ void *page_map;
+ uint32_t page_map_size;
+ off_t page_map_off;
+ void *sparse_map;
+};
+
+struct kvm_bitmap {
+ uint8_t *map;
+ u_long size;
};
/* Page table lookup constants. */
@@ -137,6 +149,11 @@ _kvm64toh(kvm_t *kd, uint64_t val)
return (be64toh(val));
}
+int _kvm_bitmap_init(struct kvm_bitmap *, u_long, u_long *);
+void _kvm_bitmap_set(struct kvm_bitmap *, u_long, unsigned int);
+int _kvm_bitmap_next(struct kvm_bitmap *, u_long *);
+void _kvm_bitmap_deinit(struct kvm_bitmap *);
+
void _kvm_err(kvm_t *kd, const char *program, const char *fmt, ...)
__printflike(3, 4);
void _kvm_freeprocs(kvm_t *kd);
@@ -154,4 +171,9 @@ 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);
-off_t _kvm_pt_find(kvm_t *, uint64_t);
+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);
+int _kvm_pmap_init(kvm_t *, uint32_t, off_t);
+void * _kvm_pmap_get(kvm_t *, u_long, size_t);
+void * _kvm_map_get(kvm_t *, u_long, unsigned int);