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
This commit is contained in:
Will Andrews 2017-11-11 23:30:58 +00:00
parent 569aaa3b97
commit c9057838be
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=325728
13 changed files with 740 additions and 122 deletions

View File

@ -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));
}

View File

@ -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_ */

View File

@ -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

View File

@ -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 */

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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 @@ _arm_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
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);

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -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 @@ _kvm_nlist(kvm_t *kd, struct kvm_nlist *nl, int initialize)
_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);
}

View File

@ -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);