aboutsummaryrefslogtreecommitdiff
path: root/lib/libkvm/kvm_minidump_arm.c
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2015-11-27 18:58:26 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2015-11-27 18:58:26 +0000
commit7f911abe54fff2adbb0dfea5a7fd8e81117d6527 (patch)
tree0b881610c55721cfde72c0eaeb1df3d9d654fd26 /lib/libkvm/kvm_minidump_arm.c
parent6a5289b068f192ee653ce6304683d0f6588f8e0e (diff)
downloadsrc-7f911abe54fff2adbb0dfea5a7fd8e81117d6527.tar.gz
src-7f911abe54fff2adbb0dfea5a7fd8e81117d6527.zip
Add support to libkvm for reading vmcores from other architectures.
- Add a kvaddr_type to represent kernel virtual addresses instead of unsigned long. - Add a struct kvm_nlist which is a stripped down version of struct nlist that uses kvaddr_t for n_value. - Add a kvm_native() routine that returns true if an open kvm descriptor is for a native kernel and memory image. - Add a kvm_open2() function similar to kvm_openfiles(). It drops the unused 'swapfile' argument and adds a new function pointer argument for a symbol resolving function. Native kernels still use _fdnlist() from libc to resolve symbols if a resolver function is not supplied, but cross kernels require a resolver. - Add a kvm_nlist2() function similar to kvm_nlist() except that it uses struct kvm_nlist instead of struct nlist. - Add a kvm_read2() function similar to kvm_read() except that it uses kvaddr_t instead of unsigned long for the kernel virtual address. - Add a new kvm_arch switch of routines needed by a vmcore backend. Each backend is responsible for implementing kvm_read2() for a given vmcore format. - Use libelf to read headers from ELF kernels and cores (except for powerpc cores). - Add internal helper routines for the common page offset hash table used by the minidump backends. - Port all of the existing kvm backends to implement a kvm_arch switch and to be cross-friendly by using private constants instead of ones that vary by platform (e.g. PAGE_SIZE). Static assertions are present when a given backend is compiled natively to ensure the private constants match the real ones. - Enable all of the existing vmcore backends on all platforms. This means that libkvm on any platform should be able to perform KVA translation and read data from a vmcore of any platform. Tested on: amd64, i386, sparc64 (marius) Differential Revision: https://reviews.freebsd.org/D3341
Notes
Notes: svn path=/head/; revision=291406
Diffstat (limited to 'lib/libkvm/kvm_minidump_arm.c')
-rw-r--r--lib/libkvm/kvm_minidump_arm.c182
1 files changed, 69 insertions, 113 deletions
diff --git a/lib/libkvm/kvm_minidump_arm.c b/lib/libkvm/kvm_minidump_arm.c
index f4ba6044e4ea..9e9cd6726804 100644
--- a/lib/libkvm/kvm_minidump_arm.c
+++ b/lib/libkvm/kvm_minidump_arm.c
@@ -33,120 +33,54 @@ __FBSDID("$FreeBSD$");
* ARM machine dependent routines for kvm and minidumps.
*/
+#include <sys/endian.h>
#include <sys/param.h>
-#ifndef CROSS_LIBKVM
-#include <sys/user.h>
-#endif
-#include <sys/proc.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <sys/fnv_hash.h>
+#include <kvm.h>
+#include <limits.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <nlist.h>
-#include <kvm.h>
-
-#ifndef CROSS_LIBKVM
-#include <vm/vm.h>
-#include <vm/vm_param.h>
-#include <machine/elf.h>
-#include <machine/cpufunc.h>
-#include <machine/minidump.h>
-#else
-#include "../../sys/arm/include/pte.h"
-#include "../../sys/arm/include/vmparam.h"
#include "../../sys/arm/include/minidump.h"
-#endif
-
-#include <limits.h>
#include "kvm_private.h"
+#include "kvm_arm.h"
-struct hpte {
- struct hpte *next;
- uint64_t pa;
- int64_t off;
-};
+#define arm_round_page(x) roundup2((kvaddr_t)(x), ARM_PAGE_SIZE)
-#define HPT_SIZE 1024
-
-/* minidump must be the first field */
struct vmstate {
- int minidump; /* 1 = minidump mode */
struct minidumphdr hdr;
- void *hpt_head[HPT_SIZE];
- uint32_t *bitmap;
+ struct hpt hpt;
void *ptemap;
+ unsigned char ei_data;
};
-static void
-hpt_insert(kvm_t *kd, uint64_t pa, int64_t off)
-{
- struct hpte *hpte;
- uint32_t fnv = FNV1_32_INIT;
-
- fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
- fnv &= (HPT_SIZE - 1);
- hpte = malloc(sizeof(*hpte));
- hpte->pa = pa;
- hpte->off = off;
- hpte->next = kd->vmst->hpt_head[fnv];
- kd->vmst->hpt_head[fnv] = hpte;
-}
-
-static int64_t
-hpt_find(kvm_t *kd, uint64_t pa)
-{
- struct hpte *hpte;
- uint32_t fnv = FNV1_32_INIT;
-
- fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
- fnv &= (HPT_SIZE - 1);
- for (hpte = kd->vmst->hpt_head[fnv]; hpte != NULL; hpte = hpte->next)
- if (pa == hpte->pa)
- return (hpte->off);
-
- return (-1);
-}
-
static int
-inithash(kvm_t *kd, uint32_t *base, int len, off_t off)
+_arm_minidump_probe(kvm_t *kd)
{
- uint64_t idx, pa;
- uint32_t bit, bits;
-
- for (idx = 0; idx < len / sizeof(*base); idx++) {
- bits = base[idx];
- while (bits) {
- bit = ffs(bits) - 1;
- bits &= ~(1ul << bit);
- pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE;
- hpt_insert(kd, pa, off);
- off += PAGE_SIZE;
- }
- }
- return (off);
+
+ return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_ARM) &&
+ _kvm_is_minidump(kd));
}
-void
-_kvm_minidump_freevtop(kvm_t *kd)
+static void
+_arm_minidump_freevtop(kvm_t *kd)
{
struct vmstate *vm = kd->vmst;
- if (vm->bitmap)
- free(vm->bitmap);
+ _kvm_hpt_free(&vm->hpt);
if (vm->ptemap)
free(vm->ptemap);
free(vm);
kd->vmst = NULL;
}
-int
-_kvm_minidump_initvtop(kvm_t *kd)
+static int
+_arm_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
+ uint32_t *bitmap;
off_t off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
@@ -156,7 +90,6 @@ _kvm_minidump_initvtop(kvm_t *kd)
}
kd->vmst = vmst;
- vmst->minidump = 1;
if (pread(kd->pmfd, &vmst->hdr,
sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
@@ -169,34 +102,41 @@ _kvm_minidump_initvtop(kvm_t *kd)
_kvm_err(kd, kd->program, "not a minidump for this platform");
return (-1);
}
+ vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version);
if (vmst->hdr.version != MINIDUMP_VERSION) {
_kvm_err(kd, kd->program, "wrong minidump version. "
"Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
return (-1);
}
+ vmst->hdr.msgbufsize = _kvm32toh(kd, vmst->hdr.msgbufsize);
+ vmst->hdr.bitmapsize = _kvm32toh(kd, vmst->hdr.bitmapsize);
+ vmst->hdr.ptesize = _kvm32toh(kd, vmst->hdr.ptesize);
+ vmst->hdr.kernbase = _kvm32toh(kd, vmst->hdr.kernbase);
/* Skip header and msgbuf */
- off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
+ off = ARM_PAGE_SIZE + arm_round_page(vmst->hdr.msgbufsize);
- vmst->bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
- if (vmst->bitmap == NULL) {
+ bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
+ if (bitmap == NULL) {
_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
"bitmap", vmst->hdr.bitmapsize);
return (-1);
}
- if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
+ if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
(ssize_t)vmst->hdr.bitmapsize) {
_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
vmst->hdr.bitmapsize);
+ free(bitmap);
return (-1);
}
- off += round_page(vmst->hdr.bitmapsize);
+ 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);
+ free(bitmap);
return (-1);
}
@@ -204,28 +144,32 @@ _kvm_minidump_initvtop(kvm_t *kd)
(ssize_t)vmst->hdr.ptesize) {
_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
vmst->hdr.ptesize);
+ free(bitmap);
return (-1);
}
off += vmst->hdr.ptesize;
/* Build physical address hash table for sparse pages */
- inithash(kd, vmst->bitmap, vmst->hdr.bitmapsize, off);
+ _kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off,
+ ARM_PAGE_SIZE, sizeof(*bitmap));
+ free(bitmap);
return (0);
}
-int
-_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
+static int
+_arm_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
{
struct vmstate *vm;
- pt_entry_t pte;
- u_long offset, pteindex, a;
+ arm_pt_entry_t pte;
+ arm_physaddr_t offset, a;
+ kvaddr_t pteindex;
off_t ofs;
- uint32_t *ptemap;
+ arm_pt_entry_t *ptemap;
if (ISALIVE(kd)) {
- _kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
+ _kvm_err(kd, 0, "_arm_minidump_kvatop called in live kernel!");
return (0);
}
@@ -233,36 +177,48 @@ _kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
ptemap = vm->ptemap;
if (va >= vm->hdr.kernbase) {
- pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
- pte = ptemap[pteindex];
+ pteindex = (va - vm->hdr.kernbase) >> ARM_PAGE_SHIFT;
+ pte = _kvm32toh(kd, ptemap[pteindex]);
if (!pte) {
- _kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
+ _kvm_err(kd, kd->program,
+ "_arm_minidump_kvatop: pte not valid");
goto invalid;
}
- if ((pte & L2_TYPE_MASK) == L2_TYPE_L) {
- offset = va & L2_L_OFFSET;
- a = pte & L2_L_FRAME;
- } else if ((pte & L2_TYPE_MASK) == L2_TYPE_S) {
- offset = va & L2_S_OFFSET;
- a = pte & L2_S_FRAME;
+ if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_L) {
+ offset = va & ARM_L2_L_OFFSET;
+ a = pte & ARM_L2_L_FRAME;
+ } else if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_S) {
+ offset = va & ARM_L2_S_OFFSET;
+ a = pte & ARM_L2_S_FRAME;
} else
goto invalid;
- ofs = hpt_find(kd, a);
+ ofs = _kvm_hpt_find(&vm->hpt, a);
if (ofs == -1) {
- _kvm_err(kd, kd->program, "_kvm_vatop: physical "
- "address 0x%lx not in minidump", a);
+ _kvm_err(kd, kd->program, "_arm_minidump_kvatop: "
+ "physical address 0x%jx not in minidump",
+ (uintmax_t)a);
goto invalid;
}
*pa = ofs + offset;
- return (PAGE_SIZE - offset);
+ return (ARM_PAGE_SIZE - offset);
} else
- _kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx "
- "not minidumped", va);
+ _kvm_err(kd, kd->program, "_arm_minidump_kvatop: virtual "
+ "address 0x%jx not minidumped", (uintmax_t)va);
invalid:
- _kvm_err(kd, 0, "invalid address (0x%lx)", va);
+ _kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
return (0);
}
+
+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,
+};
+
+KVM_ARCH(kvm_arm_minidump);