summaryrefslogtreecommitdiff
path: root/lib/libkvm
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 /lib/libkvm
parent569aaa3b9707ba1e520c2063653946b58cd5cb7c (diff)
downloadsrc-test-c9057838bea6ead0dc94d937b5882134ab5435ad.tar.gz
src-test-c9057838bea6ead0dc94d937b5882134ab5435ad.zip
libkvm: add kvm_walk_pages API.
This API allows callers to enumerate all known pages, including any direct map & kernel map virtual addresses, physical addresses, size, offset into the core, & protection configured. For architectures that support direct map addresses, also generate pages for any direct map only addresses that are not associated with kernel map addresses. Fix page size portability issue left behind from previous kvm page table lookup interface. Reviewed by: jhb Sponsored by: Backtrace I/O Differential Revision: https://reviews.freebsd.org/D12279
Notes
Notes: svn path=/head/; revision=325728
Diffstat (limited to 'lib/libkvm')
-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 a43b6a72a62e4..de30165d6ee93 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 77b118cf3c05e..5402bf9ae808b 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 c125d3c109d39..0f977820e3da3 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 38d05cd1441a8..035b70e785130 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 51eb6f8f7f709..5440db2adc69c 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 7081809faca22..e9c35df238608 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 1baca96d71f60..dbee980575f1f 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 41b99505f86d8..f3f9879f3a193 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 129a14b19d72e..3a31890d72cfb 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 96534e8d04f94..b0594490ff401 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 9fba6fb8e6e86..715117d59fb90 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 4f35fa09bcd24..01611f7d4b157 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 03ae4cefb8158..03c7ee45a6208 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);