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
This commit is contained in:
parent
6a5289b068
commit
7f911abe54
@ -1819,6 +1819,7 @@ _prebuild_libs+= lib/libc++
|
||||
.endif
|
||||
|
||||
lib/libgeom__L: lib/libexpat__L
|
||||
lib/libkvm__L: lib/libelf__L
|
||||
|
||||
.if ${MK_LIBTHR} != "no"
|
||||
_lib_libthr= lib/libthr
|
||||
|
@ -9,11 +9,7 @@ BULIBS= ${OBJ_BU}/libbfd/libbfd.a ${OBJ_BU}/libopcodes/libopcodes.a \
|
||||
GDBLIBS= ${OBJ_GDB}/libgdb/libgdb.a
|
||||
|
||||
DPADD= ${GDBLIBS} ${BULIBS} ${LIBKVM}
|
||||
LDADD= ${GDBLIBS} ${BULIBS} -lkvm${GDB_SUFFIX}
|
||||
LDADD= ${GDBLIBS} ${BULIBS} -lkvm
|
||||
LIBADD+= m readline ncursesw gnuregex
|
||||
|
||||
.if defined(GDB_CROSS_DEBUGGER)
|
||||
CFLAGS+= -Wl,-export-dynamic
|
||||
.endif
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
@ -41,9 +41,6 @@ __FBSDID("$FreeBSD$");
|
||||
#include <kvm.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
#ifdef CROSS_DEBUGGER
|
||||
#include <proc_service.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -81,24 +78,6 @@ static struct ui_file *parse_gdberr;
|
||||
|
||||
static void (*kgdb_new_objfile_chain)(struct objfile * objfile);
|
||||
|
||||
#ifdef CROSS_DEBUGGER
|
||||
ps_err_e
|
||||
ps_pglobal_lookup(struct ps_prochandle *ph, const char *obj, const char *name,
|
||||
psaddr_t *sym_addr)
|
||||
{
|
||||
struct minimal_symbol *ms;
|
||||
CORE_ADDR addr;
|
||||
|
||||
ms = lookup_minimal_symbol (name, NULL, NULL);
|
||||
if (ms == NULL)
|
||||
return PS_NOSYM;
|
||||
|
||||
addr = SYMBOL_VALUE_ADDRESS (ms);
|
||||
store_typed_address(sym_addr, builtin_type_void_data_ptr, addr);
|
||||
return PS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
|
@ -78,6 +78,19 @@ static char kvm_err[_POSIX2_LINE_MAX];
|
||||
#define KERNOFF (kgdb_kernbase ())
|
||||
#define PINKERNEL(x) ((x) >= KERNOFF)
|
||||
|
||||
static int
|
||||
kgdb_resolve_symbol(const char *name, kvaddr_t *kva)
|
||||
{
|
||||
struct minimal_symbol *ms;
|
||||
|
||||
ms = lookup_minimal_symbol (name, NULL, NULL);
|
||||
if (ms == NULL)
|
||||
return (1);
|
||||
|
||||
*kva = SYMBOL_VALUE_ADDRESS (ms);
|
||||
return (0);;
|
||||
}
|
||||
|
||||
static CORE_ADDR
|
||||
kgdb_kernbase (void)
|
||||
{
|
||||
@ -120,8 +133,8 @@ kgdb_trgt_open(char *filename, int from_tty)
|
||||
|
||||
old_chain = make_cleanup (xfree, filename);
|
||||
|
||||
nkvm = kvm_openfiles(bfd_get_filename(exec_bfd), filename, NULL,
|
||||
write_files ? O_RDWR : O_RDONLY, kvm_err);
|
||||
nkvm = kvm_open2(bfd_get_filename(exec_bfd), filename,
|
||||
write_files ? O_RDWR : O_RDONLY, kvm_err, kgdb_resolve_symbol);
|
||||
if (nkvm == NULL)
|
||||
error ("Failed to open vmcore: %s", kvm_err);
|
||||
|
||||
@ -254,7 +267,7 @@ kgdb_trgt_xfer_memory(CORE_ADDR memaddr, char *myaddr, int len, int write,
|
||||
if (len == 0)
|
||||
return (0);
|
||||
if (!write)
|
||||
return (kvm_read(kvm, memaddr, myaddr, len));
|
||||
return (kvm_read2(kvm, memaddr, myaddr, len));
|
||||
else
|
||||
return (kvm_write(kvm, memaddr, myaddr, len));
|
||||
}
|
||||
|
@ -1,50 +1,38 @@
|
||||
# @(#)Makefile 8.1 (Berkeley) 6/4/93
|
||||
# $FreeBSD$
|
||||
|
||||
.if defined(TARGET_ARCH) && !defined(COMPAT_32BIT)
|
||||
KVM_XARCH=${TARGET_ARCH}
|
||||
KVM_XCPUARCH=${KVM_XARCH:C/mips(n32|64)?(el)?/mips/:C/arm(v6)?(eb|hf)?/arm/:C/powerpc64/powerpc/}
|
||||
.else
|
||||
KVM_XARCH=${MACHINE_ARCH}
|
||||
KVM_XCPUARCH=${MACHINE_CPUARCH}
|
||||
.endif
|
||||
|
||||
.if ${KVM_XARCH} != ${MACHINE_ARCH}
|
||||
LIB= kvm-${KVM_XARCH}
|
||||
CFLAGS+=-DCROSS_LIBKVM
|
||||
.else
|
||||
LIB= kvm
|
||||
.endif
|
||||
|
||||
SHLIBDIR?= /lib
|
||||
SHLIB_MAJOR= 6
|
||||
CFLAGS+=-DLIBC_SCCS -I${.CURDIR}
|
||||
|
||||
.if exists(${.CURDIR}/kvm_${KVM_XARCH}.c)
|
||||
KVM_ARCH=${KVM_XARCH}
|
||||
.else
|
||||
KVM_ARCH=${KVM_XCPUARCH}
|
||||
.endif
|
||||
|
||||
WARNS?= 3
|
||||
|
||||
SRCS= kvm.c kvm_${KVM_ARCH}.c kvm_cptime.c kvm_file.c kvm_getloadavg.c \
|
||||
kvm_getswapinfo.c kvm_pcpu.c kvm_proc.c kvm_vnet.c
|
||||
.if exists(${.CURDIR}/kvm_minidump_${KVM_ARCH}.c)
|
||||
SRCS+= kvm_minidump_${KVM_ARCH}.c
|
||||
.endif
|
||||
SRCS= kvm.c kvm_cptime.c kvm_file.c kvm_getloadavg.c \
|
||||
kvm_getswapinfo.c kvm_pcpu.c kvm_proc.c kvm_vnet.c \
|
||||
kvm_minidump_aarch64.c \
|
||||
kvm_amd64.c kvm_minidump_amd64.c \
|
||||
kvm_arm.c kvm_minidump_arm.c \
|
||||
kvm_i386.c kvm_minidump_i386.c \
|
||||
kvm_minidump_mips.c \
|
||||
kvm_powerpc.c kvm_powerpc64.c \
|
||||
kvm_sparc64.c
|
||||
INCS= kvm.h
|
||||
|
||||
LIBADD= elf
|
||||
|
||||
MAN= kvm.3 kvm_getcptime.3 kvm_geterr.3 kvm_getfiles.3 kvm_getloadavg.3 \
|
||||
kvm_getpcpu.3 kvm_getprocs.3 kvm_getswapinfo.3 kvm_nlist.3 kvm_open.3 \
|
||||
kvm_read.3
|
||||
kvm_getpcpu.3 kvm_getprocs.3 kvm_getswapinfo.3 kvm_native.3 \
|
||||
kvm_nlist.3 kvm_open.3 kvm_read.3
|
||||
|
||||
MLINKS+=kvm_getpcpu.3 kvm_getmaxcpu.3 \
|
||||
kvm_getpcpu.3 kvm_dpcpu_setcpu.3 \
|
||||
kvm_getpcpu.3 kvm_read_zpcpu.3 \
|
||||
kvm_getpcpu.3 kvm_counter_u64_fetch.3
|
||||
MLINKS+=kvm_getprocs.3 kvm_getargv.3 kvm_getprocs.3 kvm_getenvv.3
|
||||
MLINKS+=kvm_open.3 kvm_close.3 kvm_open.3 kvm_openfiles.3
|
||||
MLINKS+=kvm_read.3 kvm_write.3
|
||||
MLINKS+=kvm_nlist.3 kvm_nlist2.3
|
||||
MLINKS+=kvm_open.3 kvm_close.3 kvm_open.3 kvm_open2.3 kvm_open.3 kvm_openfiles.3
|
||||
MLINKS+=kvm_read.3 kvm_read2.3 kvm_read.3 kvm_write.3
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
|
@ -9,6 +9,7 @@ DIRDEPS = \
|
||||
lib/${CSU_DIR} \
|
||||
lib/libc \
|
||||
lib/libcompiler_rt \
|
||||
lib/libelf \
|
||||
|
||||
|
||||
.include <dirdeps.mk>
|
||||
|
@ -32,7 +32,7 @@
|
||||
.\" @(#)kvm.3 8.1 (Berkeley) 6/4/93
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd April 25, 2010
|
||||
.Dd November 27, 2015
|
||||
.Dt KVM 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -101,6 +101,44 @@ routine,
|
||||
to return (not print out) the error message
|
||||
corresponding to the most recent error condition on the
|
||||
given descriptor.
|
||||
.Sh CROSS DEBUGGING
|
||||
The
|
||||
.Nm
|
||||
library supports inspection of crash dumps from non-native kernels.
|
||||
Only a limited subset of the kvm interface is supported for these dumps.
|
||||
To inspect a crash dump of a non-native kernel,
|
||||
the caller must provide a
|
||||
.Fa resolver
|
||||
function when opening a descriptor via
|
||||
.Fn kvm_open2 .
|
||||
In addition,
|
||||
the kvm interface defines an integer type
|
||||
.Pq Vt kvaddr_t
|
||||
that is large enough to hold all valid addresses of all supported
|
||||
architectures.
|
||||
The interface also defines a new namelist structure type
|
||||
.Pq Vt "struct kvm_nlist"
|
||||
for use with
|
||||
.Fn kvm_nlist2 .
|
||||
To avoid address truncation issues,
|
||||
the caller should use
|
||||
.Fn kvm_nlist2
|
||||
and
|
||||
.Fn kvm_read2
|
||||
in place of
|
||||
.Fn kvm_nlist
|
||||
and
|
||||
.Fn kvm_read ,
|
||||
respectively.
|
||||
Finally, only a limited subset of operations are supported for non-native
|
||||
crash dumps:
|
||||
.Fn kvm_close ,
|
||||
.Fn kvm_geterr
|
||||
.Fn kvm_open2 ,
|
||||
.Fn kvm_native ,
|
||||
.Fn kvm_nlist2 ,
|
||||
and
|
||||
.Fn kvm_read2 .
|
||||
.Sh SEE ALSO
|
||||
.Xr kvm_close 3 ,
|
||||
.Xr kvm_getargv 3 ,
|
||||
@ -110,10 +148,14 @@ given descriptor.
|
||||
.Xr kvm_getloadavg 3 ,
|
||||
.Xr kvm_getprocs 3 ,
|
||||
.Xr kvm_getswapinfo 3 ,
|
||||
.Xr kvm_native 3 ,
|
||||
.Xr kvm_nlist 3 ,
|
||||
.Xr kvm_nlist2 3 ,
|
||||
.Xr kvm_open 3 ,
|
||||
.Xr kvm_open2 3 ,
|
||||
.Xr kvm_openfiles 3 ,
|
||||
.Xr kvm_read 3 ,
|
||||
.Xr kvm_read2 3 ,
|
||||
.Xr kvm_write 3 ,
|
||||
.Xr sysctl 3 ,
|
||||
.Xr kmem 4 ,
|
||||
|
387
lib/libkvm/kvm.c
387
lib/libkvm/kvm.c
@ -41,62 +41,63 @@ static char sccsid[] = "@(#)kvm.c 8.2 (Berkeley) 2/13/94";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/fnv_hash.h>
|
||||
|
||||
#define _WANT_VNET
|
||||
|
||||
#include <sys/user.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/linker.h>
|
||||
#include <sys/pcpu.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <net/vnet.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
|
||||
#include <machine/vmparam.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <kvm.h>
|
||||
#include <limits.h>
|
||||
#include <nlist.h>
|
||||
#include <paths.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "kvm_private.h"
|
||||
|
||||
#ifndef CROSS_LIBKVM
|
||||
SET_DECLARE(kvm_arch, struct kvm_arch);
|
||||
|
||||
/* from src/lib/libc/gen/nlist.c */
|
||||
int __fdnlist(int, struct nlist *);
|
||||
|
||||
#define kvm_fdnlist __fdnlist
|
||||
|
||||
#else
|
||||
|
||||
#include <proc_service.h>
|
||||
|
||||
static int
|
||||
kvm_fdnlist(int fd, struct nlist *list)
|
||||
kvm_fdnlist(kvm_t *kd, struct kvm_nlist *list)
|
||||
{
|
||||
psaddr_t addr;
|
||||
ps_err_e pserr;
|
||||
int nfail;
|
||||
kvaddr_t addr;
|
||||
int error, nfail;
|
||||
|
||||
if (kd->resolve_symbol == NULL) {
|
||||
struct nlist *nl;
|
||||
int count, i;
|
||||
|
||||
for (count = 0; list[count].n_name != NULL &&
|
||||
list[count].n_name[0] != '\0'; count++)
|
||||
;
|
||||
nl = calloc(count + 1, sizeof(*nl));
|
||||
for (i = 0; i < count; i++)
|
||||
nl[i].n_name = list[i].n_name;
|
||||
nfail = __fdnlist(kd->nlfd, nl);
|
||||
for (i = 0; i < count; i++) {
|
||||
list[i].n_type = nl[i].n_type;
|
||||
list[i].n_value = nl[i].n_value;
|
||||
}
|
||||
free(nl);
|
||||
return (nfail);
|
||||
}
|
||||
|
||||
nfail = 0;
|
||||
while (list->n_name != NULL && list->n_name[0] != '\0') {
|
||||
list->n_other = 0;
|
||||
list->n_desc = 0;
|
||||
pserr = ps_pglobal_lookup(NULL, NULL, list->n_name, &addr);
|
||||
if (pserr != PS_OK) {
|
||||
error = kd->resolve_symbol(list->n_name, &addr);
|
||||
if (error != 0) {
|
||||
nfail++;
|
||||
list->n_value = 0;
|
||||
list->n_type = 0;
|
||||
@ -109,8 +110,6 @@ kvm_fdnlist(int fd, struct nlist *list)
|
||||
return (nfail);
|
||||
}
|
||||
|
||||
#endif /* CROSS_LIBKVM */
|
||||
|
||||
char *
|
||||
kvm_geterr(kvm_t *kd)
|
||||
{
|
||||
@ -175,9 +174,206 @@ _kvm_malloc(kvm_t *kd, size_t n)
|
||||
return (p);
|
||||
}
|
||||
|
||||
static int
|
||||
_kvm_read_kernel_ehdr(kvm_t *kd)
|
||||
{
|
||||
Elf *elf;
|
||||
|
||||
if (elf_version(EV_CURRENT) == EV_NONE) {
|
||||
_kvm_err(kd, kd->program, "Unsupported libelf");
|
||||
return (-1);
|
||||
}
|
||||
elf = elf_begin(kd->nlfd, ELF_C_READ, NULL);
|
||||
if (elf == NULL) {
|
||||
_kvm_err(kd, kd->program, "%s", elf_errmsg(0));
|
||||
return (-1);
|
||||
}
|
||||
if (elf_kind(elf) != ELF_K_ELF) {
|
||||
_kvm_err(kd, kd->program, "kernel is not an ELF file");
|
||||
return (-1);
|
||||
}
|
||||
if (gelf_getehdr(elf, &kd->nlehdr) == NULL) {
|
||||
_kvm_err(kd, kd->program, "%s", elf_errmsg(0));
|
||||
elf_end(elf);
|
||||
return (-1);
|
||||
}
|
||||
elf_end(elf);
|
||||
|
||||
switch (kd->nlehdr.e_ident[EI_DATA]) {
|
||||
case ELFDATA2LSB:
|
||||
case ELFDATA2MSB:
|
||||
return (0);
|
||||
default:
|
||||
_kvm_err(kd, kd->program,
|
||||
"unsupported ELF data encoding for kernel");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_probe_elf_kernel(kvm_t *kd, int class, int machine)
|
||||
{
|
||||
|
||||
return (kd->nlehdr.e_ident[EI_CLASS] == class &&
|
||||
kd->nlehdr.e_type == ET_EXEC &&
|
||||
kd->nlehdr.e_machine == machine);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_is_minidump(kvm_t *kd)
|
||||
{
|
||||
char minihdr[8];
|
||||
|
||||
if (kd->rawdump)
|
||||
return (0);
|
||||
if (pread(kd->pmfd, &minihdr, 8, 0) == 8 &&
|
||||
memcmp(&minihdr, "minidump", 8) == 0)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The powerpc backend has a hack to strip a leading kerneldump
|
||||
* header from the core before treating it as an ELF header.
|
||||
*
|
||||
* We can add that here if we can get a change to libelf to support
|
||||
* an inital offset into the file. Alternatively we could patch
|
||||
* savecore to extract cores from a regular file instead.
|
||||
*/
|
||||
int
|
||||
_kvm_read_core_phdrs(kvm_t *kd, size_t *phnump, GElf_Phdr **phdrp)
|
||||
{
|
||||
GElf_Ehdr ehdr;
|
||||
GElf_Phdr *phdr;
|
||||
Elf *elf;
|
||||
size_t i, phnum;
|
||||
|
||||
elf = elf_begin(kd->pmfd, ELF_C_READ, NULL);
|
||||
if (elf == NULL) {
|
||||
_kvm_err(kd, kd->program, "%s", elf_errmsg(0));
|
||||
return (-1);
|
||||
}
|
||||
if (elf_kind(elf) != ELF_K_ELF) {
|
||||
_kvm_err(kd, kd->program, "invalid core");
|
||||
goto bad;
|
||||
}
|
||||
if (gelf_getclass(elf) != kd->nlehdr.e_ident[EI_CLASS]) {
|
||||
_kvm_err(kd, kd->program, "invalid core");
|
||||
goto bad;
|
||||
}
|
||||
if (gelf_getehdr(elf, &ehdr) == NULL) {
|
||||
_kvm_err(kd, kd->program, "%s", elf_errmsg(0));
|
||||
goto bad;
|
||||
}
|
||||
if (ehdr.e_type != ET_CORE) {
|
||||
_kvm_err(kd, kd->program, "invalid core");
|
||||
goto bad;
|
||||
}
|
||||
if (ehdr.e_machine != kd->nlehdr.e_machine) {
|
||||
_kvm_err(kd, kd->program, "invalid core");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (elf_getphdrnum(elf, &phnum) == -1) {
|
||||
_kvm_err(kd, kd->program, "%s", elf_errmsg(0));
|
||||
goto bad;
|
||||
}
|
||||
|
||||
phdr = calloc(phnum, sizeof(*phdr));
|
||||
if (phdr == NULL) {
|
||||
_kvm_err(kd, kd->program, "failed to allocate phdrs");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
for (i = 0; i < phnum; i++) {
|
||||
if (gelf_getphdr(elf, i, &phdr[i]) == NULL) {
|
||||
_kvm_err(kd, kd->program, "%s", elf_errmsg(0));
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
elf_end(elf);
|
||||
*phnump = phnum;
|
||||
*phdrp = phdr;
|
||||
return (0);
|
||||
|
||||
bad:
|
||||
elf_end(elf);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static void
|
||||
_kvm_hpt_insert(struct hpt *hpt, uint64_t pa, off_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 = hpt->hpt_head[fnv];
|
||||
hpt->hpt_head[fnv] = hpte;
|
||||
}
|
||||
|
||||
void
|
||||
_kvm_hpt_init(kvm_t *kd, struct hpt *hpt, void *base, size_t len, off_t off,
|
||||
int page_size, int word_size)
|
||||
{
|
||||
uint64_t bits, idx, pa;
|
||||
uint64_t *base64;
|
||||
uint32_t *base32;
|
||||
|
||||
base64 = base;
|
||||
base32 = base;
|
||||
for (idx = 0; idx < len / word_size; idx++) {
|
||||
if (word_size == sizeof(uint64_t))
|
||||
bits = _kvm64toh(kd, base64[idx]);
|
||||
else
|
||||
bits = _kvm32toh(kd, base32[idx]);
|
||||
pa = idx * word_size * NBBY * page_size;
|
||||
for (; bits != 0; bits >>= 1, pa += page_size) {
|
||||
if ((bits & 1) == 0)
|
||||
continue;
|
||||
_kvm_hpt_insert(hpt, pa, off);
|
||||
off += page_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
off_t
|
||||
_kvm_hpt_find(struct hpt *hpt, 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 = hpt->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) {
|
||||
if (pa == hpte->pa)
|
||||
return (hpte->off);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
void
|
||||
_kvm_hpt_free(struct hpt *hpt)
|
||||
{
|
||||
struct hpte *hpte, *next;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HPT_SIZE; i++) {
|
||||
for (hpte = hpt->hpt_head[i]; hpte != NULL; hpte = next) {
|
||||
next = hpte->next;
|
||||
free(hpte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static kvm_t *
|
||||
_kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout)
|
||||
{
|
||||
struct kvm_arch **parch;
|
||||
struct stat st;
|
||||
|
||||
kd->vmfd = -1;
|
||||
@ -235,16 +431,40 @@ _kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout)
|
||||
}
|
||||
/*
|
||||
* This is a crash dump.
|
||||
* Initialize the virtual address translation machinery,
|
||||
* but first setup the namelist fd.
|
||||
* Open the namelist fd and determine the architecture.
|
||||
*/
|
||||
if ((kd->nlfd = open(uf, O_RDONLY | O_CLOEXEC, 0)) < 0) {
|
||||
_kvm_syserr(kd, kd->program, "%s", uf);
|
||||
goto failed;
|
||||
}
|
||||
if (_kvm_read_kernel_ehdr(kd) < 0)
|
||||
goto failed;
|
||||
if (strncmp(mf, _PATH_FWMEM, strlen(_PATH_FWMEM)) == 0)
|
||||
kd->rawdump = 1;
|
||||
if (_kvm_initvtop(kd) < 0)
|
||||
SET_FOREACH(parch, kvm_arch) {
|
||||
if ((*parch)->ka_probe(kd)) {
|
||||
kd->arch = *parch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (kd->arch == NULL) {
|
||||
_kvm_err(kd, kd->program, "unsupported architecture");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-native kernels require a symbol resolver.
|
||||
*/
|
||||
if (!kd->arch->ka_native(kd) && kd->resolve_symbol == NULL) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"non-native kernel requires a symbol resolver");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the virtual address translation machinery.
|
||||
*/
|
||||
if (kd->arch->ka_initvtop(kd) < 0)
|
||||
goto failed;
|
||||
return (kd);
|
||||
failed:
|
||||
@ -267,7 +487,6 @@ kvm_openfiles(const char *uf, const char *mf, const char *sf __unused, int flag,
|
||||
(void)strlcpy(errout, strerror(errno), _POSIX2_LINE_MAX);
|
||||
return (0);
|
||||
}
|
||||
kd->program = 0;
|
||||
return (_kvm_open(kd, uf, mf, flag, errout));
|
||||
}
|
||||
|
||||
@ -287,19 +506,33 @@ kvm_open(const char *uf, const char *mf, const char *sf __unused, int flag,
|
||||
return (_kvm_open(kd, uf, mf, flag, NULL));
|
||||
}
|
||||
|
||||
kvm_t *
|
||||
kvm_open2(const char *uf, const char *mf, int flag, char *errout,
|
||||
int (*resolver)(const char *, kvaddr_t *))
|
||||
{
|
||||
kvm_t *kd;
|
||||
|
||||
if ((kd = calloc(1, sizeof(*kd))) == NULL) {
|
||||
(void)strlcpy(errout, strerror(errno), _POSIX2_LINE_MAX);
|
||||
return (0);
|
||||
}
|
||||
kd->resolve_symbol = resolver;
|
||||
return (_kvm_open(kd, uf, mf, flag, errout));
|
||||
}
|
||||
|
||||
int
|
||||
kvm_close(kvm_t *kd)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (kd->vmst != NULL)
|
||||
kd->arch->ka_freevtop(kd);
|
||||
if (kd->pmfd >= 0)
|
||||
error |= close(kd->pmfd);
|
||||
if (kd->vmfd >= 0)
|
||||
error |= close(kd->vmfd);
|
||||
if (kd->nlfd >= 0)
|
||||
error |= close(kd->nlfd);
|
||||
if (kd->vmst)
|
||||
_kvm_freevtop(kd);
|
||||
if (kd->procbase != 0)
|
||||
free((void *)kd->procbase);
|
||||
if (kd->argbuf != 0)
|
||||
@ -318,10 +551,10 @@ kvm_close(kvm_t *kd)
|
||||
* symbol names, try again, and merge back what we could resolve.
|
||||
*/
|
||||
static int
|
||||
kvm_fdnlist_prefix(kvm_t *kd, struct nlist *nl, int missing, const char *prefix,
|
||||
uintptr_t (*validate_fn)(kvm_t *, uintptr_t))
|
||||
kvm_fdnlist_prefix(kvm_t *kd, struct kvm_nlist *nl, int missing,
|
||||
const char *prefix, kvaddr_t (*validate_fn)(kvm_t *, kvaddr_t))
|
||||
{
|
||||
struct nlist *n, *np, *p;
|
||||
struct kvm_nlist *n, *np, *p;
|
||||
char *cp, *ce;
|
||||
const char *ccp;
|
||||
size_t len;
|
||||
@ -337,14 +570,14 @@ kvm_fdnlist_prefix(kvm_t *kd, struct nlist *nl, int missing, const char *prefix,
|
||||
for (p = nl; p->n_name && p->n_name[0]; ++p) {
|
||||
if (p->n_type != N_UNDF)
|
||||
continue;
|
||||
len += sizeof(struct nlist) + strlen(prefix) +
|
||||
len += sizeof(struct kvm_nlist) + strlen(prefix) +
|
||||
2 * (strlen(p->n_name) + 1);
|
||||
unresolved++;
|
||||
}
|
||||
if (unresolved == 0)
|
||||
return (unresolved);
|
||||
/* Add space for the terminating nlist entry. */
|
||||
len += sizeof(struct nlist);
|
||||
len += sizeof(struct kvm_nlist);
|
||||
unresolved++;
|
||||
|
||||
/* Alloc one chunk for (nlist, [names]) and setup pointers. */
|
||||
@ -353,7 +586,7 @@ kvm_fdnlist_prefix(kvm_t *kd, struct nlist *nl, int missing, const char *prefix,
|
||||
if (n == NULL)
|
||||
return (missing);
|
||||
cp = ce = (char *)np;
|
||||
cp += unresolved * sizeof(struct nlist);
|
||||
cp += unresolved * sizeof(struct kvm_nlist);
|
||||
ce += len;
|
||||
|
||||
/* Generate shortened nlist with special prefix. */
|
||||
@ -361,7 +594,7 @@ kvm_fdnlist_prefix(kvm_t *kd, struct nlist *nl, int missing, const char *prefix,
|
||||
for (p = nl; p->n_name && p->n_name[0]; ++p) {
|
||||
if (p->n_type != N_UNDF)
|
||||
continue;
|
||||
bcopy(p, np, sizeof(struct nlist));
|
||||
*np = *p;
|
||||
/* Save the new\0orig. name so we can later match it again. */
|
||||
slen = snprintf(cp, ce - cp, "%s%s%c%s", prefix,
|
||||
(prefix[0] != '\0' && p->n_name[0] == '_') ?
|
||||
@ -376,7 +609,7 @@ kvm_fdnlist_prefix(kvm_t *kd, struct nlist *nl, int missing, const char *prefix,
|
||||
|
||||
/* Do lookup on the reduced list. */
|
||||
np = n;
|
||||
unresolved = kvm_fdnlist(kd->nlfd, np);
|
||||
unresolved = kvm_fdnlist(kd, np);
|
||||
|
||||
/* Check if we could resolve further symbols and update the list. */
|
||||
if (unresolved >= 0 && unresolved < missing) {
|
||||
@ -398,8 +631,6 @@ kvm_fdnlist_prefix(kvm_t *kd, struct nlist *nl, int missing, const char *prefix,
|
||||
continue;
|
||||
/* Update nlist with new, translated results. */
|
||||
p->n_type = np->n_type;
|
||||
p->n_other = np->n_other;
|
||||
p->n_desc = np->n_desc;
|
||||
if (validate_fn)
|
||||
p->n_value = (*validate_fn)(kd, np->n_value);
|
||||
else
|
||||
@ -418,9 +649,9 @@ kvm_fdnlist_prefix(kvm_t *kd, struct nlist *nl, int missing, const char *prefix,
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_nlist(kvm_t *kd, struct nlist *nl, int initialize)
|
||||
_kvm_nlist(kvm_t *kd, struct kvm_nlist *nl, int initialize)
|
||||
{
|
||||
struct nlist *p;
|
||||
struct kvm_nlist *p;
|
||||
int nvalid;
|
||||
struct kld_sym_lookup lookup;
|
||||
int error;
|
||||
@ -433,7 +664,7 @@ _kvm_nlist(kvm_t *kd, struct nlist *nl, int initialize)
|
||||
* slow library call.
|
||||
*/
|
||||
if (!ISALIVE(kd)) {
|
||||
error = kvm_fdnlist(kd->nlfd, nl);
|
||||
error = kvm_fdnlist(kd, nl);
|
||||
if (error <= 0) /* Hard error or success. */
|
||||
return (error);
|
||||
|
||||
@ -475,8 +706,6 @@ _kvm_nlist(kvm_t *kd, struct nlist *nl, int initialize)
|
||||
|
||||
if (kldsym(0, KLDSYM_LOOKUP, &lookup) != -1) {
|
||||
p->n_type = N_TEXT;
|
||||
p->n_other = 0;
|
||||
p->n_desc = 0;
|
||||
if (_kvm_vnet_initialized(kd, initialize) &&
|
||||
strcmp(prefix, VNET_SYMPREFIX) == 0)
|
||||
p->n_value =
|
||||
@ -519,7 +748,7 @@ _kvm_nlist(kvm_t *kd, struct nlist *nl, int initialize)
|
||||
}
|
||||
|
||||
int
|
||||
kvm_nlist(kvm_t *kd, struct nlist *nl)
|
||||
kvm_nlist2(kvm_t *kd, struct kvm_nlist *nl)
|
||||
{
|
||||
|
||||
/*
|
||||
@ -529,8 +758,48 @@ kvm_nlist(kvm_t *kd, struct nlist *nl)
|
||||
return (_kvm_nlist(kd, nl, 1));
|
||||
}
|
||||
|
||||
int
|
||||
kvm_nlist(kvm_t *kd, struct nlist *nl)
|
||||
{
|
||||
struct kvm_nlist *kl;
|
||||
int count, i, nfail;
|
||||
|
||||
/*
|
||||
* Avoid reporting truncated addresses by failing for non-native
|
||||
* cores.
|
||||
*/
|
||||
if (!kvm_native(kd)) {
|
||||
_kvm_err(kd, kd->program, "kvm_nlist of non-native vmcore");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
for (count = 0; nl[count].n_name != NULL && nl[count].n_name[0] != '\0';
|
||||
count++)
|
||||
;
|
||||
if (count == 0)
|
||||
return (0);
|
||||
kl = calloc(count + 1, sizeof(*kl));
|
||||
for (i = 0; i < count; i++)
|
||||
kl[i].n_name = nl[i].n_name;
|
||||
nfail = kvm_nlist2(kd, kl);
|
||||
for (i = 0; i < count; i++) {
|
||||
nl[i].n_type = kl[i].n_type;
|
||||
nl[i].n_other = 0;
|
||||
nl[i].n_desc = 0;
|
||||
nl[i].n_value = kl[i].n_value;
|
||||
}
|
||||
return (nfail);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
kvm_read(kvm_t *kd, u_long kva, void *buf, size_t len)
|
||||
{
|
||||
|
||||
return (kvm_read2(kd, kva, buf, len));
|
||||
}
|
||||
|
||||
ssize_t
|
||||
kvm_read2(kvm_t *kd, kvaddr_t kva, void *buf, size_t len)
|
||||
{
|
||||
int cc;
|
||||
ssize_t cr;
|
||||
@ -544,7 +813,8 @@ kvm_read(kvm_t *kd, u_long kva, void *buf, size_t len)
|
||||
*/
|
||||
errno = 0;
|
||||
if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) {
|
||||
_kvm_err(kd, 0, "invalid address (%lx)", kva);
|
||||
_kvm_err(kd, 0, "invalid address (0x%jx)",
|
||||
(uintmax_t)kva);
|
||||
return (-1);
|
||||
}
|
||||
cr = read(kd->vmfd, buf, len);
|
||||
@ -558,7 +828,7 @@ kvm_read(kvm_t *kd, u_long kva, void *buf, size_t len)
|
||||
|
||||
cp = buf;
|
||||
while (len > 0) {
|
||||
cc = _kvm_kvatop(kd, kva, &pa);
|
||||
cc = kd->arch->ka_kvatop(kd, kva, &pa);
|
||||
if (cc == 0)
|
||||
return (-1);
|
||||
if (cc > (ssize_t)len)
|
||||
@ -574,7 +844,7 @@ kvm_read(kvm_t *kd, u_long kva, void *buf, size_t len)
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If kvm_kvatop returns a bogus value or our core file is
|
||||
* If ka_kvatop returns a bogus value or our core file is
|
||||
* truncated, we might wind up seeking beyond the end of the
|
||||
* core file in which case the read will return 0 (EOF).
|
||||
*/
|
||||
@ -616,3 +886,12 @@ kvm_write(kvm_t *kd, u_long kva, const void *buf, size_t len)
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
int
|
||||
kvm_native(kvm_t *kd)
|
||||
{
|
||||
|
||||
if (ISALIVE(kd))
|
||||
return (1);
|
||||
return (kd->arch->ka_native(kd));
|
||||
}
|
||||
|
@ -51,6 +51,14 @@ typedef __ssize_t ssize_t;
|
||||
#define _SSIZE_T_DECLARED
|
||||
#endif
|
||||
|
||||
typedef uint64_t kvaddr_t; /* An address in a target image. */
|
||||
|
||||
struct kvm_nlist {
|
||||
const char *n_name;
|
||||
unsigned char n_type;
|
||||
kvaddr_t n_value;
|
||||
};
|
||||
|
||||
typedef struct __kvm kvm_t;
|
||||
|
||||
struct kinfo_proc;
|
||||
@ -83,13 +91,19 @@ uint64_t kvm_counter_u64_fetch(kvm_t *, u_long);
|
||||
struct kinfo_proc *
|
||||
kvm_getprocs(kvm_t *, int, int, int *);
|
||||
int kvm_getswapinfo(kvm_t *, struct kvm_swap *, int, int);
|
||||
int kvm_native(kvm_t *);
|
||||
int kvm_nlist(kvm_t *, struct nlist *);
|
||||
int kvm_nlist2(kvm_t *, struct kvm_nlist *);
|
||||
kvm_t *kvm_open
|
||||
(const char *, const char *, const char *, int, const char *);
|
||||
kvm_t *kvm_openfiles
|
||||
(const char *, const char *, const char *, int, char *);
|
||||
kvm_t *kvm_open2
|
||||
(const char *, const char *, int, char *,
|
||||
int (*)(const char *, kvaddr_t *));
|
||||
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);
|
||||
__END_DECLS
|
||||
|
||||
|
@ -1,105 +0,0 @@
|
||||
/*-
|
||||
* Copyright (C) 2006 Bruce M. Simpson.
|
||||
* Copyright (c) 2015 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed by Andrew Turner under
|
||||
* sponsorship from the FreeBSD Foundation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* arm64 (AArch64) machine dependent routines for kvm.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <machine/pmap.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <kvm.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "kvm_private.h"
|
||||
|
||||
/* minidump must be the first item! */
|
||||
struct vmstate {
|
||||
int minidump; /* 1 = minidump mode */
|
||||
void *mmapbase;
|
||||
size_t mmapsize;
|
||||
};
|
||||
|
||||
void
|
||||
_kvm_freevtop(kvm_t *kd)
|
||||
{
|
||||
|
||||
if (kd->vmst != 0) {
|
||||
if (kd->vmst->minidump)
|
||||
return (_kvm_minidump_freevtop(kd));
|
||||
if (kd->vmst->mmapbase != NULL)
|
||||
munmap(kd->vmst->mmapbase, kd->vmst->mmapsize);
|
||||
free(kd->vmst);
|
||||
kd->vmst = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_initvtop(kvm_t *kd)
|
||||
{
|
||||
char minihdr[8];
|
||||
|
||||
if (!kd->rawdump) {
|
||||
if (pread(kd->pmfd, &minihdr, 8, 0) == 8) {
|
||||
if (memcmp(&minihdr, "minidump", 8) == 0)
|
||||
return (_kvm_minidump_initvtop(kd));
|
||||
} else {
|
||||
_kvm_err(kd, kd->program, "cannot read header");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
_kvm_err(kd, 0, "_kvm_initvtop: Unsupported image type");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
{
|
||||
|
||||
if (kd->vmst->minidump)
|
||||
return _kvm_minidump_kvatop(kd, va, pa);
|
||||
|
||||
|
||||
_kvm_err(kd, 0, "_kvm_kvatop: Unsupported image type");
|
||||
return (0);
|
||||
}
|
61
lib/libkvm/kvm_aarch64.h
Normal file
61
lib/libkvm/kvm_aarch64.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 John H. Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __KVM_AARCH64_H__
|
||||
#define __KVM_AARCH64_H__
|
||||
|
||||
#ifdef __aarch64__
|
||||
#include <machine/pte.h>
|
||||
#endif
|
||||
|
||||
typedef uint64_t aarch64_physaddr_t;
|
||||
typedef uint64_t aarch64_pte_t;
|
||||
|
||||
#define AARCH64_PAGE_SHIFT 12
|
||||
#define AARCH64_PAGE_SIZE (1 << AARCH64_PAGE_SHIFT)
|
||||
#define AARCH64_PAGE_MASK (AARCH64_PAGE_SIZE - 1)
|
||||
|
||||
#define AARCH64_ATTR_MASK 0xfff0000000000fff
|
||||
|
||||
#define AARCH64_ATTR_DESCR_MASK 3
|
||||
|
||||
#define AARCH64_L3_SHIFT 12
|
||||
#define AARCH64_L3_PAGE 0x3
|
||||
|
||||
#ifdef __aarch64__
|
||||
_Static_assert(PAGE_SHIFT == AARCH64_PAGE_SHIFT, "PAGE_SHIFT mismatch");
|
||||
_Static_assert(PAGE_SIZE == AARCH64_PAGE_SIZE, "PAGE_SIZE mismatch");
|
||||
_Static_assert(PAGE_MASK == AARCH64_PAGE_MASK, "PAGE_MASK mismatch");
|
||||
_Static_assert(ATTR_MASK == AARCH64_ATTR_MASK, "ATTR_MASK mismatch");
|
||||
_Static_assert(ATTR_DESCR_MASK == AARCH64_ATTR_DESCR_MASK,
|
||||
"ATTR_DESCR_MASK mismatch");
|
||||
_Static_assert(L3_SHIFT == AARCH64_L3_SHIFT, "L3_SHIFT mismatch");
|
||||
_Static_assert(L3_PAGE == AARCH64_L3_PAGE, "L3_PAGE mismatch");
|
||||
#endif
|
||||
|
||||
#endif /* !__KVM_AARCH64_H__ */
|
@ -46,117 +46,76 @@ static char sccsid[] = "@(#)kvm_hp300.c 8.1 (Berkeley) 6/4/93";
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/endian.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <nlist.h>
|
||||
#include <kvm.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
|
||||
#include <machine/elf.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "kvm_private.h"
|
||||
#include "kvm_amd64.h"
|
||||
|
||||
#ifndef btop
|
||||
#define btop(x) (amd64_btop(x))
|
||||
#define ptob(x) (amd64_ptob(x))
|
||||
#endif
|
||||
|
||||
/* minidump must be the first item! */
|
||||
struct vmstate {
|
||||
int minidump; /* 1 = minidump mode */
|
||||
void *mmapbase;
|
||||
size_t mmapsize;
|
||||
pml4_entry_t *PML4;
|
||||
size_t phnum;
|
||||
GElf_Phdr *phdr;
|
||||
amd64_pml4e_t *PML4;
|
||||
};
|
||||
|
||||
/*
|
||||
* Map the ELF headers into the process' address space. We do this in two
|
||||
* steps: first the ELF header itself and using that information the whole
|
||||
* set of headers.
|
||||
*/
|
||||
static int
|
||||
_kvm_maphdrs(kvm_t *kd, size_t sz)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
|
||||
/* munmap() previous mmap(). */
|
||||
if (vm->mmapbase != NULL) {
|
||||
munmap(vm->mmapbase, vm->mmapsize);
|
||||
vm->mmapbase = NULL;
|
||||
}
|
||||
|
||||
vm->mmapsize = sz;
|
||||
vm->mmapbase = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
||||
if (vm->mmapbase == MAP_FAILED) {
|
||||
_kvm_err(kd, kd->program, "cannot mmap corefile");
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate a physical memory address to a file-offset in the crash-dump.
|
||||
*/
|
||||
static size_t
|
||||
_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs)
|
||||
{
|
||||
Elf_Ehdr *e = kd->vmst->mmapbase;
|
||||
Elf_Phdr *p;
|
||||
int n;
|
||||
struct vmstate *vm = kd->vmst;
|
||||
GElf_Phdr *p;
|
||||
size_t n;
|
||||
|
||||
if (kd->rawdump) {
|
||||
*ofs = pa;
|
||||
return (PAGE_SIZE - ((size_t)pa & PAGE_MASK));
|
||||
return (AMD64_PAGE_SIZE - (pa & AMD64_PAGE_MASK));
|
||||
}
|
||||
|
||||
p = (Elf_Phdr*)((char*)e + e->e_phoff);
|
||||
n = e->e_phnum;
|
||||
p = vm->phdr;
|
||||
n = vm->phnum;
|
||||
while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
|
||||
p++, n--;
|
||||
if (n == 0)
|
||||
return (0);
|
||||
*ofs = (pa - p->p_paddr) + p->p_offset;
|
||||
return (PAGE_SIZE - ((size_t)pa & PAGE_MASK));
|
||||
return (AMD64_PAGE_SIZE - (pa & AMD64_PAGE_MASK));
|
||||
}
|
||||
|
||||
void
|
||||
_kvm_freevtop(kvm_t *kd)
|
||||
static void
|
||||
_amd64_freevtop(kvm_t *kd)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
|
||||
if (kd->vmst->minidump)
|
||||
return (_kvm_minidump_freevtop(kd));
|
||||
if (vm->mmapbase != NULL)
|
||||
munmap(vm->mmapbase, vm->mmapsize);
|
||||
if (vm->PML4)
|
||||
free(vm->PML4);
|
||||
free(vm->phdr);
|
||||
free(vm);
|
||||
kd->vmst = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_initvtop(kvm_t *kd)
|
||||
static int
|
||||
_amd64_probe(kvm_t *kd)
|
||||
{
|
||||
struct nlist nl[2];
|
||||
u_long pa;
|
||||
u_long kernbase;
|
||||
pml4_entry_t *PML4;
|
||||
Elf_Ehdr *ehdr;
|
||||
size_t hdrsz;
|
||||
char minihdr[8];
|
||||
|
||||
if (!kd->rawdump && pread(kd->pmfd, &minihdr, 8, 0) == 8)
|
||||
if (memcmp(&minihdr, "minidump", 8) == 0)
|
||||
return (_kvm_minidump_initvtop(kd));
|
||||
return (_kvm_probe_elf_kernel(kd, ELFCLASS64, EM_X86_64) &&
|
||||
!_kvm_is_minidump(kd));
|
||||
}
|
||||
|
||||
static int
|
||||
_amd64_initvtop(kvm_t *kd)
|
||||
{
|
||||
struct kvm_nlist nl[2];
|
||||
amd64_physaddr_t pa;
|
||||
kvaddr_t kernbase;
|
||||
amd64_pml4e_t *PML4;
|
||||
|
||||
kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
|
||||
if (kd->vmst == 0) {
|
||||
@ -166,19 +125,15 @@ _kvm_initvtop(kvm_t *kd)
|
||||
kd->vmst->PML4 = 0;
|
||||
|
||||
if (kd->rawdump == 0) {
|
||||
if (_kvm_maphdrs(kd, sizeof(Elf_Ehdr)) == -1)
|
||||
return (-1);
|
||||
|
||||
ehdr = kd->vmst->mmapbase;
|
||||
hdrsz = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum;
|
||||
if (_kvm_maphdrs(kd, hdrsz) == -1)
|
||||
if (_kvm_read_core_phdrs(kd, &kd->vmst->phnum,
|
||||
&kd->vmst->phdr) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
nl[0].n_name = "kernbase";
|
||||
nl[1].n_name = 0;
|
||||
|
||||
if (kvm_nlist(kd, nl) != 0) {
|
||||
if (kvm_nlist2(kd, nl) != 0) {
|
||||
_kvm_err(kd, kd->program, "bad namelist - no kernbase");
|
||||
return (-1);
|
||||
}
|
||||
@ -187,17 +142,18 @@ _kvm_initvtop(kvm_t *kd)
|
||||
nl[0].n_name = "KPML4phys";
|
||||
nl[1].n_name = 0;
|
||||
|
||||
if (kvm_nlist(kd, nl) != 0) {
|
||||
if (kvm_nlist2(kd, nl) != 0) {
|
||||
_kvm_err(kd, kd->program, "bad namelist - no KPML4phys");
|
||||
return (-1);
|
||||
}
|
||||
if (kvm_read(kd, (nl[0].n_value - kernbase), &pa, sizeof(pa)) !=
|
||||
if (kvm_read2(kd, (nl[0].n_value - kernbase), &pa, sizeof(pa)) !=
|
||||
sizeof(pa)) {
|
||||
_kvm_err(kd, kd->program, "cannot read KPML4phys");
|
||||
return (-1);
|
||||
}
|
||||
PML4 = _kvm_malloc(kd, PAGE_SIZE);
|
||||
if (kvm_read(kd, pa, PML4, PAGE_SIZE) != PAGE_SIZE) {
|
||||
pa = le64toh(pa);
|
||||
PML4 = _kvm_malloc(kd, AMD64_PAGE_SIZE);
|
||||
if (kvm_read2(kd, pa, PML4, AMD64_PAGE_SIZE) != AMD64_PAGE_SIZE) {
|
||||
_kvm_err(kd, kd->program, "cannot read KPML4phys");
|
||||
return (-1);
|
||||
}
|
||||
@ -206,27 +162,27 @@ _kvm_initvtop(kvm_t *kd)
|
||||
}
|
||||
|
||||
static int
|
||||
_kvm_vatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
_amd64_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
struct vmstate *vm;
|
||||
u_long offset;
|
||||
u_long pdpe_pa;
|
||||
u_long pde_pa;
|
||||
u_long pte_pa;
|
||||
pml4_entry_t pml4e;
|
||||
pdp_entry_t pdpe;
|
||||
pd_entry_t pde;
|
||||
pt_entry_t pte;
|
||||
u_long pml4eindex;
|
||||
u_long pdpeindex;
|
||||
u_long pdeindex;
|
||||
u_long pteindex;
|
||||
u_long a;
|
||||
amd64_physaddr_t offset;
|
||||
amd64_physaddr_t pdpe_pa;
|
||||
amd64_physaddr_t pde_pa;
|
||||
amd64_physaddr_t pte_pa;
|
||||
amd64_pml4e_t pml4e;
|
||||
amd64_pdpe_t pdpe;
|
||||
amd64_pde_t pde;
|
||||
amd64_pte_t pte;
|
||||
kvaddr_t pml4eindex;
|
||||
kvaddr_t pdpeindex;
|
||||
kvaddr_t pdeindex;
|
||||
kvaddr_t pteindex;
|
||||
amd64_physaddr_t a;
|
||||
off_t ofs;
|
||||
size_t s;
|
||||
|
||||
vm = kd->vmst;
|
||||
offset = va & (PAGE_SIZE - 1);
|
||||
offset = va & AMD64_PAGE_MASK;
|
||||
|
||||
/*
|
||||
* If we are initializing (kernel page table descriptor pointer
|
||||
@ -236,121 +192,141 @@ _kvm_vatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
s = _kvm_pa2off(kd, va, pa);
|
||||
if (s == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_kvm_vatop: bootstrap data not in dump");
|
||||
"_amd64_vatop: bootstrap data not in dump");
|
||||
goto invalid;
|
||||
} else
|
||||
return (PAGE_SIZE - offset);
|
||||
return (AMD64_PAGE_SIZE - offset);
|
||||
}
|
||||
|
||||
pml4eindex = (va >> PML4SHIFT) & (NPML4EPG - 1);
|
||||
pml4e = vm->PML4[pml4eindex];
|
||||
if (((u_long)pml4e & PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pml4e not valid");
|
||||
pml4eindex = (va >> AMD64_PML4SHIFT) & (AMD64_NPML4EPG - 1);
|
||||
pml4e = le64toh(vm->PML4[pml4eindex]);
|
||||
if ((pml4e & AMD64_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_amd64_vatop: pml4e not valid");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
pdpeindex = (va >> PDPSHIFT) & (NPDPEPG-1);
|
||||
pdpe_pa = ((u_long)pml4e & PG_FRAME) +
|
||||
(pdpeindex * sizeof(pdp_entry_t));
|
||||
pdpeindex = (va >> AMD64_PDPSHIFT) & (AMD64_NPDPEPG - 1);
|
||||
pdpe_pa = (pml4e & AMD64_PG_FRAME) + (pdpeindex * sizeof(amd64_pdpe_t));
|
||||
|
||||
s = _kvm_pa2off(kd, pdpe_pa, &ofs);
|
||||
if (s < sizeof pdpe) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pdpe_pa not found");
|
||||
if (s < sizeof(pdpe)) {
|
||||
_kvm_err(kd, kd->program, "_amd64_vatop: pdpe_pa not found");
|
||||
goto invalid;
|
||||
}
|
||||
if (lseek(kd->pmfd, ofs, 0) == -1) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: lseek pdpe_pa");
|
||||
if (pread(kd->pmfd, &pdpe, sizeof(pdpe), ofs) != sizeof(pdpe)) {
|
||||
_kvm_syserr(kd, kd->program, "_amd64_vatop: read pdpe");
|
||||
goto invalid;
|
||||
}
|
||||
if (read(kd->pmfd, &pdpe, sizeof pdpe) != sizeof pdpe) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: read pdpe");
|
||||
goto invalid;
|
||||
}
|
||||
if (((u_long)pdpe & PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pdpe not valid");
|
||||
pdpe = le64toh(pdpe);
|
||||
if ((pdpe & AMD64_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_amd64_vatop: pdpe not valid");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
pdeindex = (va >> PDRSHIFT) & (NPDEPG-1);
|
||||
pde_pa = ((u_long)pdpe & PG_FRAME) + (pdeindex * sizeof(pd_entry_t));
|
||||
|
||||
s = _kvm_pa2off(kd, pde_pa, &ofs);
|
||||
if (s < sizeof pde) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: pde_pa not found");
|
||||
goto invalid;
|
||||
}
|
||||
if (lseek(kd->pmfd, ofs, 0) == -1) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: lseek pde_pa");
|
||||
goto invalid;
|
||||
}
|
||||
if (read(kd->pmfd, &pde, sizeof pde) != sizeof pde) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: read pde");
|
||||
goto invalid;
|
||||
}
|
||||
if (((u_long)pde & PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pde not valid");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if ((u_long)pde & PG_PS) {
|
||||
/*
|
||||
* No final-level page table; ptd describes one 2MB page.
|
||||
*/
|
||||
#define PAGE2M_MASK (NBPDR - 1)
|
||||
#define PG_FRAME2M (~PAGE2M_MASK)
|
||||
a = ((u_long)pde & PG_FRAME2M) + (va & PAGE2M_MASK);
|
||||
if (pdpe & AMD64_PG_PS) {
|
||||
/*
|
||||
* No next-level page table; pdpe describes one 1GB page.
|
||||
*/
|
||||
a = (pde & AMD64_PG_1GB_FRAME) + (va & AMD64_PDPMASK);
|
||||
s = _kvm_pa2off(kd, a, pa);
|
||||
if (s == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_kvm_vatop: 2MB page address not in dump");
|
||||
"_amd64_vatop: 1GB page address not in dump");
|
||||
goto invalid;
|
||||
} else
|
||||
return (NBPDR - (va & PAGE2M_MASK));
|
||||
return (AMD64_NBPDP - (va & AMD64_PDPMASK));
|
||||
}
|
||||
|
||||
pteindex = (va >> PAGE_SHIFT) & (NPTEPG-1);
|
||||
pte_pa = ((u_long)pde & PG_FRAME) + (pteindex * sizeof(pt_entry_t));
|
||||
pdeindex = (va >> AMD64_PDRSHIFT) & (AMD64_NPDEPG - 1);
|
||||
pde_pa = (pdpe & AMD64_PG_FRAME) + (pdeindex * sizeof(amd64_pde_t));
|
||||
|
||||
s = _kvm_pa2off(kd, pde_pa, &ofs);
|
||||
if (s < sizeof(pde)) {
|
||||
_kvm_syserr(kd, kd->program, "_amd64_vatop: pde_pa not found");
|
||||
goto invalid;
|
||||
}
|
||||
if (pread(kd->pmfd, &pde, sizeof(pde), ofs) != sizeof(pde)) {
|
||||
_kvm_syserr(kd, kd->program, "_amd64_vatop: read pde");
|
||||
goto invalid;
|
||||
}
|
||||
pde = le64toh(pde);
|
||||
if ((pde & AMD64_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_amd64_vatop: pde not valid");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (pde & AMD64_PG_PS) {
|
||||
/*
|
||||
* No final-level page table; pde describes one 2MB page.
|
||||
*/
|
||||
a = (pde & AMD64_PG_PS_FRAME) + (va & AMD64_PDRMASK);
|
||||
s = _kvm_pa2off(kd, a, pa);
|
||||
if (s == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_amd64_vatop: 2MB page address not in dump");
|
||||
goto invalid;
|
||||
} else
|
||||
return (AMD64_NBPDR - (va & AMD64_PDRMASK));
|
||||
}
|
||||
|
||||
pteindex = (va >> AMD64_PAGE_SHIFT) & (AMD64_NPTEPG - 1);
|
||||
pte_pa = (pde & AMD64_PG_FRAME) + (pteindex * sizeof(amd64_pte_t));
|
||||
|
||||
s = _kvm_pa2off(kd, pte_pa, &ofs);
|
||||
if (s < sizeof pte) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte_pa not found");
|
||||
if (s < sizeof(pte)) {
|
||||
_kvm_err(kd, kd->program, "_amd64_vatop: pte_pa not found");
|
||||
goto invalid;
|
||||
}
|
||||
if (lseek(kd->pmfd, ofs, 0) == -1) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: lseek");
|
||||
if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
|
||||
_kvm_syserr(kd, kd->program, "_amd64_vatop: read");
|
||||
goto invalid;
|
||||
}
|
||||
if (read(kd->pmfd, &pte, sizeof pte) != sizeof pte) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: read");
|
||||
goto invalid;
|
||||
}
|
||||
if (((u_long)pte & PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
|
||||
if ((pte & AMD64_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_amd64_vatop: pte not valid");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
a = ((u_long)pte & PG_FRAME) + offset;
|
||||
a = (pte & AMD64_PG_FRAME) + offset;
|
||||
s = _kvm_pa2off(kd, a, pa);
|
||||
if (s == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: address not in dump");
|
||||
_kvm_err(kd, kd->program, "_amd64_vatop: address not in dump");
|
||||
goto invalid;
|
||||
} else
|
||||
return (PAGE_SIZE - offset);
|
||||
return (AMD64_PAGE_SIZE - offset);
|
||||
|
||||
invalid:
|
||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
||||
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
static int
|
||||
_amd64_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
|
||||
if (kd->vmst->minidump)
|
||||
return (_kvm_minidump_kvatop(kd, va, pa));
|
||||
if (ISALIVE(kd)) {
|
||||
_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
|
||||
return (0);
|
||||
}
|
||||
return (_kvm_vatop(kd, va, pa));
|
||||
return (_amd64_vatop(kd, va, pa));
|
||||
}
|
||||
|
||||
int
|
||||
_amd64_native(kvm_t *kd)
|
||||
{
|
||||
|
||||
#ifdef __amd64__
|
||||
return (1);
|
||||
#else
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct kvm_arch kvm_amd64 = {
|
||||
.ka_probe = _amd64_probe,
|
||||
.ka_initvtop = _amd64_initvtop,
|
||||
.ka_freevtop = _amd64_freevtop,
|
||||
.ka_kvatop = _amd64_kvatop,
|
||||
.ka_native = _amd64_native,
|
||||
};
|
||||
|
||||
KVM_ARCH(kvm_amd64);
|
||||
|
88
lib/libkvm/kvm_amd64.h
Normal file
88
lib/libkvm/kvm_amd64.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 John H. Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __KVM_AMD64_H__
|
||||
#define __KVM_AMD64_H__
|
||||
|
||||
#ifdef __amd64__
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
#endif
|
||||
|
||||
typedef uint64_t amd64_physaddr_t;
|
||||
typedef uint64_t amd64_pte_t;
|
||||
typedef uint64_t amd64_pde_t;
|
||||
typedef uint64_t amd64_pdpe_t;
|
||||
typedef uint64_t amd64_pml4e_t;
|
||||
|
||||
#define AMD64_NPTEPG (AMD64_PAGE_SIZE / sizeof(amd64_pte_t))
|
||||
#define AMD64_PAGE_SHIFT 12
|
||||
#define AMD64_PAGE_SIZE (1 << AMD64_PAGE_SHIFT)
|
||||
#define AMD64_PAGE_MASK (AMD64_PAGE_SIZE - 1)
|
||||
#define AMD64_NPDEPG (AMD64_PAGE_SIZE / sizeof(amd64_pde_t))
|
||||
#define AMD64_PDRSHIFT 21
|
||||
#define AMD64_NBPDR (1 << AMD64_PDRSHIFT)
|
||||
#define AMD64_PDRMASK (AMD64_NBPDR - 1)
|
||||
#define AMD64_NPDPEPG (AMD64_PAGE_SIZE / sizeof(amd64_pdpe_t))
|
||||
#define AMD64_PDPSHIFT 30
|
||||
#define AMD64_NBPDP (1 << AMD64_PDPSHIFT)
|
||||
#define AMD64_PDPMASK (AMD64_NBPDP - 1)
|
||||
#define AMD64_NPML4EPG (AMD64_PAGE_SIZE / sizeof(amd64_pml4e_t))
|
||||
#define AMD64_PML4SHIFT 39
|
||||
|
||||
#define AMD64_PG_V 0x001
|
||||
#define AMD64_PG_PS 0x080
|
||||
#define AMD64_PG_FRAME (0x000ffffffffff000)
|
||||
#define AMD64_PG_PS_FRAME (0x000fffffffe00000)
|
||||
#define AMD64_PG_1GB_FRAME (0x000fffffc0000000)
|
||||
|
||||
#ifdef __amd64__
|
||||
_Static_assert(NPTEPG == AMD64_NPTEPG, "NPTEPG mismatch");
|
||||
_Static_assert(PAGE_SHIFT == AMD64_PAGE_SHIFT, "PAGE_SHIFT mismatch");
|
||||
_Static_assert(PAGE_SIZE == AMD64_PAGE_SIZE, "PAGE_SIZE mismatch");
|
||||
_Static_assert(PAGE_MASK == AMD64_PAGE_MASK, "PAGE_MASK mismatch");
|
||||
_Static_assert(NPDEPG == AMD64_NPDEPG, "NPDEPG mismatch");
|
||||
_Static_assert(PDRSHIFT == AMD64_PDRSHIFT, "PDRSHIFT mismatch");
|
||||
_Static_assert(NBPDR == AMD64_NBPDR, "NBPDR mismatch");
|
||||
_Static_assert(PDRMASK == AMD64_PDRMASK, "PDRMASK mismatch");
|
||||
_Static_assert(NPDPEPG == AMD64_NPDPEPG, "NPDPEPG mismatch");
|
||||
_Static_assert(PDPSHIFT == AMD64_PDPSHIFT, "PDPSHIFT mismatch");
|
||||
_Static_assert(NBPDP == AMD64_NBPDP, "NBPDP mismatch");
|
||||
_Static_assert(PDPMASK == AMD64_PDPMASK, "PDPMASK mismatch");
|
||||
_Static_assert(NPML4EPG == AMD64_NPML4EPG, "NPML4EPG mismatch");
|
||||
_Static_assert(PML4SHIFT == AMD64_PML4SHIFT, "PML4SHIFT mismatch");
|
||||
|
||||
_Static_assert(PG_V == AMD64_PG_V, "PG_V mismatch");
|
||||
_Static_assert(PG_PS == AMD64_PG_PS, "PG_PS mismatch");
|
||||
_Static_assert(PG_FRAME == AMD64_PG_FRAME, "PG_FRAME mismatch");
|
||||
_Static_assert(PG_PS_FRAME == AMD64_PG_PS_FRAME, "PG_PS_FRAME mismatch");
|
||||
#endif
|
||||
|
||||
int _amd64_native(kvm_t *);
|
||||
|
||||
#endif /* !__KVM_AMD64_H__ */
|
@ -39,67 +39,38 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/elf32.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#ifndef CROSS_LIBKVM
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/pmap.h>
|
||||
#include <machine/pmap.h>
|
||||
#else
|
||||
#include "../../sys/arm/include/pte.h"
|
||||
#include "../../sys/arm/include/vmparam.h"
|
||||
#endif
|
||||
|
||||
#include <db.h>
|
||||
#include <limits.h>
|
||||
#include <sys/endian.h>
|
||||
#include <kvm.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __arm__
|
||||
#include <machine/vmparam.h>
|
||||
#endif
|
||||
|
||||
#include "kvm_private.h"
|
||||
#include "kvm_arm.h"
|
||||
|
||||
/* minidump must be the first item! */
|
||||
struct vmstate {
|
||||
int minidump; /* 1 = minidump mode */
|
||||
pd_entry_t *l1pt;
|
||||
void *mmapbase;
|
||||
size_t mmapsize;
|
||||
arm_pd_entry_t *l1pt;
|
||||
size_t phnum;
|
||||
GElf_Phdr *phdr;
|
||||
};
|
||||
|
||||
static int
|
||||
_kvm_maphdrs(kvm_t *kd, size_t sz)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
|
||||
/* munmap() previous mmap(). */
|
||||
if (vm->mmapbase != NULL) {
|
||||
munmap(vm->mmapbase, vm->mmapsize);
|
||||
vm->mmapbase = NULL;
|
||||
}
|
||||
|
||||
vm->mmapsize = sz;
|
||||
vm->mmapbase = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
||||
if (vm->mmapbase == MAP_FAILED) {
|
||||
_kvm_err(kd, kd->program, "cannot mmap corefile");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate a physical memory address to a file-offset in the crash-dump.
|
||||
*/
|
||||
static size_t
|
||||
_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs, size_t pgsz)
|
||||
{
|
||||
Elf32_Ehdr *e = kd->vmst->mmapbase;
|
||||
Elf32_Phdr *p = (Elf32_Phdr*)((char*)e + e->e_phoff);
|
||||
int n = e->e_phnum;
|
||||
struct vmstate *vm = kd->vmst;
|
||||
GElf_Phdr *p;
|
||||
size_t n;
|
||||
|
||||
p = vm->phdr;
|
||||
n = vm->phnum;
|
||||
while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
|
||||
p++, n--;
|
||||
if (n == 0)
|
||||
@ -111,40 +82,38 @@ _kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs, size_t pgsz)
|
||||
return (pgsz - ((size_t)pa & (pgsz - 1)));
|
||||
}
|
||||
|
||||
void
|
||||
_kvm_freevtop(kvm_t *kd)
|
||||
static void
|
||||
_arm_freevtop(kvm_t *kd)
|
||||
{
|
||||
if (kd->vmst != 0) {
|
||||
if (kd->vmst->minidump)
|
||||
return (_kvm_minidump_freevtop(kd));
|
||||
if (kd->vmst->mmapbase != NULL)
|
||||
munmap(kd->vmst->mmapbase, kd->vmst->mmapsize);
|
||||
free(kd->vmst);
|
||||
kd->vmst = NULL;
|
||||
}
|
||||
struct vmstate *vm = kd->vmst;
|
||||
|
||||
free(vm->phdr);
|
||||
free(vm);
|
||||
kd->vmst = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_initvtop(kvm_t *kd)
|
||||
static int
|
||||
_arm_probe(kvm_t *kd)
|
||||
{
|
||||
|
||||
return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_ARM) &&
|
||||
!_kvm_is_minidump(kd));
|
||||
}
|
||||
|
||||
static int
|
||||
_arm_initvtop(kvm_t *kd)
|
||||
{
|
||||
struct vmstate *vm;
|
||||
struct nlist nl[2];
|
||||
u_long kernbase, physaddr, pa;
|
||||
pd_entry_t *l1pt;
|
||||
Elf32_Ehdr *ehdr;
|
||||
Elf32_Phdr *phdr;
|
||||
size_t hdrsz;
|
||||
char minihdr[8];
|
||||
int found, i;
|
||||
struct kvm_nlist nl[2];
|
||||
kvaddr_t kernbase;
|
||||
arm_physaddr_t physaddr, pa;
|
||||
arm_pd_entry_t *l1pt;
|
||||
size_t i;
|
||||
int found;
|
||||
|
||||
if (!kd->rawdump) {
|
||||
if (pread(kd->pmfd, &minihdr, 8, 0) == 8) {
|
||||
if (memcmp(&minihdr, "minidump", 8) == 0)
|
||||
return (_kvm_minidump_initvtop(kd));
|
||||
} else {
|
||||
_kvm_err(kd, kd->program, "cannot read header");
|
||||
return (-1);
|
||||
}
|
||||
if (kd->rawdump) {
|
||||
_kvm_err(kd, kd->program, "raw dumps not supported on arm");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
vm = _kvm_malloc(kd, sizeof(*vm));
|
||||
@ -154,19 +123,15 @@ _kvm_initvtop(kvm_t *kd)
|
||||
}
|
||||
kd->vmst = vm;
|
||||
vm->l1pt = NULL;
|
||||
if (_kvm_maphdrs(kd, sizeof(Elf32_Ehdr)) == -1)
|
||||
return (-1);
|
||||
ehdr = kd->vmst->mmapbase;
|
||||
hdrsz = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum;
|
||||
if (_kvm_maphdrs(kd, hdrsz) == -1)
|
||||
|
||||
if (_kvm_read_core_phdrs(kd, &vm->phnum, &vm->phdr) == -1)
|
||||
return (-1);
|
||||
|
||||
phdr = (Elf32_Phdr *)((uint8_t *)ehdr + ehdr->e_phoff);
|
||||
found = 0;
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
if (phdr[i].p_type == PT_DUMP_DELTA) {
|
||||
kernbase = phdr[i].p_vaddr;
|
||||
physaddr = phdr[i].p_paddr;
|
||||
for (i = 0; i < vm->phnum; i++) {
|
||||
if (vm->phdr[i].p_type == PT_DUMP_DELTA) {
|
||||
kernbase = vm->phdr[i].p_vaddr;
|
||||
physaddr = vm->phdr[i].p_paddr;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
@ -175,30 +140,35 @@ _kvm_initvtop(kvm_t *kd)
|
||||
nl[1].n_name = NULL;
|
||||
if (!found) {
|
||||
nl[0].n_name = "kernbase";
|
||||
if (kvm_nlist(kd, nl) != 0)
|
||||
if (kvm_nlist2(kd, nl) != 0) {
|
||||
#ifdef __arm__
|
||||
kernbase = KERNBASE;
|
||||
else
|
||||
#else
|
||||
_kvm_err(kd, kd->program, "cannot resolve kernbase");
|
||||
return (-1);
|
||||
#endif
|
||||
} else
|
||||
kernbase = nl[0].n_value;
|
||||
|
||||
nl[0].n_name = "physaddr";
|
||||
if (kvm_nlist(kd, nl) != 0) {
|
||||
if (kvm_nlist2(kd, nl) != 0) {
|
||||
_kvm_err(kd, kd->program, "couldn't get phys addr");
|
||||
return (-1);
|
||||
}
|
||||
physaddr = nl[0].n_value;
|
||||
}
|
||||
nl[0].n_name = "kernel_l1pa";
|
||||
if (kvm_nlist(kd, nl) != 0) {
|
||||
if (kvm_nlist2(kd, nl) != 0) {
|
||||
_kvm_err(kd, kd->program, "bad namelist");
|
||||
return (-1);
|
||||
}
|
||||
if (kvm_read(kd, (nl[0].n_value - kernbase + physaddr), &pa,
|
||||
if (kvm_read2(kd, (nl[0].n_value - kernbase + physaddr), &pa,
|
||||
sizeof(pa)) != sizeof(pa)) {
|
||||
_kvm_err(kd, kd->program, "cannot read kernel_l1pa");
|
||||
return (-1);
|
||||
}
|
||||
l1pt = _kvm_malloc(kd, L1_TABLE_SIZE);
|
||||
if (kvm_read(kd, pa, l1pt, L1_TABLE_SIZE) != L1_TABLE_SIZE) {
|
||||
l1pt = _kvm_malloc(kd, ARM_L1_TABLE_SIZE);
|
||||
if (kvm_read2(kd, pa, l1pt, ARM_L1_TABLE_SIZE) != ARM_L1_TABLE_SIZE) {
|
||||
_kvm_err(kd, kd->program, "cannot read l1pt");
|
||||
free(l1pt);
|
||||
return (-1);
|
||||
@ -208,62 +178,51 @@ _kvm_initvtop(kvm_t *kd)
|
||||
}
|
||||
|
||||
/* from arm/pmap.c */
|
||||
#define L1_IDX(va) (((vm_offset_t)(va)) >> L1_S_SHIFT)
|
||||
/* from arm/pmap.h */
|
||||
#define L1_TYPE_INV 0x00 /* Invalid (fault) */
|
||||
#define L1_TYPE_C 0x01 /* Coarse L2 */
|
||||
#define L1_TYPE_S 0x02 /* Section */
|
||||
#define L1_TYPE_F 0x03 /* Fine L2 */
|
||||
#define L1_TYPE_MASK 0x03 /* mask of type bits */
|
||||
#define ARM_L1_IDX(va) ((va) >> ARM_L1_S_SHIFT)
|
||||
|
||||
#define l1pte_section_p(pde) (((pde) & L1_TYPE_MASK) == L1_TYPE_S)
|
||||
#define l1pte_section_p(pde) (((pde) & ARM_L1_TYPE_MASK) == ARM_L1_TYPE_S)
|
||||
#define l1pte_valid(pde) ((pde) != 0)
|
||||
#define l2pte_valid(pte) ((pte) != 0)
|
||||
#define l2pte_index(v) (((v) & L2_ADDR_BITS) >> L2_S_SHIFT)
|
||||
#define l2pte_index(v) (((v) & ARM_L2_ADDR_BITS) >> ARM_L2_S_SHIFT)
|
||||
|
||||
|
||||
int
|
||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
static int
|
||||
_arm_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
pd_entry_t pd;
|
||||
pt_entry_t pte;
|
||||
off_t pte_pa;
|
||||
|
||||
if (kd->vmst->minidump)
|
||||
return (_kvm_minidump_kvatop(kd, va, pa));
|
||||
arm_pd_entry_t pd;
|
||||
arm_pt_entry_t pte;
|
||||
arm_physaddr_t pte_pa;
|
||||
off_t pte_off;
|
||||
|
||||
if (vm->l1pt == NULL)
|
||||
return (_kvm_pa2off(kd, va, pa, PAGE_SIZE));
|
||||
pd = vm->l1pt[L1_IDX(va)];
|
||||
return (_kvm_pa2off(kd, va, pa, ARM_PAGE_SIZE));
|
||||
pd = _kvm32toh(kd, vm->l1pt[ARM_L1_IDX(va)]);
|
||||
if (!l1pte_valid(pd))
|
||||
goto invalid;
|
||||
if (l1pte_section_p(pd)) {
|
||||
/* 1MB section mapping. */
|
||||
*pa = ((u_long)pd & L1_S_ADDR_MASK) + (va & L1_S_OFFSET);
|
||||
return (_kvm_pa2off(kd, *pa, pa, L1_S_SIZE));
|
||||
*pa = (pd & ARM_L1_S_ADDR_MASK) + (va & ARM_L1_S_OFFSET);
|
||||
return (_kvm_pa2off(kd, *pa, pa, ARM_L1_S_SIZE));
|
||||
}
|
||||
pte_pa = (pd & L1_ADDR_MASK) + l2pte_index(va) * sizeof(pte);
|
||||
_kvm_pa2off(kd, pte_pa, &pte_pa, L1_S_SIZE);
|
||||
if (lseek(kd->pmfd, pte_pa, 0) == -1) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_kvatop: lseek");
|
||||
goto invalid;
|
||||
}
|
||||
if (read(kd->pmfd, &pte, sizeof(pte)) != sizeof (pte)) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_kvatop: read");
|
||||
pte_pa = (pd & ARM_L1_C_ADDR_MASK) + l2pte_index(va) * sizeof(pte);
|
||||
_kvm_pa2off(kd, pte_pa, &pte_off, ARM_L1_S_SIZE);
|
||||
if (pread(kd->pmfd, &pte, sizeof(pte), pte_off) != sizeof(pte)) {
|
||||
_kvm_syserr(kd, kd->program, "_arm_kvatop: pread");
|
||||
goto invalid;
|
||||
}
|
||||
pte = _kvm32toh(kd, pte);
|
||||
if (!l2pte_valid(pte)) {
|
||||
goto invalid;
|
||||
}
|
||||
if ((pte & L2_TYPE_MASK) == L2_TYPE_L) {
|
||||
*pa = (pte & L2_L_FRAME) | (va & L2_L_OFFSET);
|
||||
return (_kvm_pa2off(kd, *pa, pa, L2_L_SIZE));
|
||||
if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_L) {
|
||||
*pa = (pte & ARM_L2_L_FRAME) | (va & ARM_L2_L_OFFSET);
|
||||
return (_kvm_pa2off(kd, *pa, pa, ARM_L2_L_SIZE));
|
||||
}
|
||||
*pa = (pte & L2_S_FRAME) | (va & L2_S_OFFSET);
|
||||
return (_kvm_pa2off(kd, *pa, pa, PAGE_SIZE));
|
||||
*pa = (pte & ARM_L2_S_FRAME) | (va & ARM_L2_S_OFFSET);
|
||||
return (_kvm_pa2off(kd, *pa, pa, ARM_PAGE_SIZE));
|
||||
invalid:
|
||||
_kvm_err(kd, 0, "Invalid address (%lx)", va);
|
||||
_kvm_err(kd, 0, "Invalid address (%jx)", (uintmax_t)va);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -284,3 +243,28 @@ _kvm_mdopen(kvm_t *kd)
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
_arm_native(kvm_t *kd)
|
||||
{
|
||||
|
||||
#ifdef __arm__
|
||||
#if _BYTE_ORDER == _LITTLE_ENDIAN
|
||||
return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2LSB);
|
||||
#else
|
||||
return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2MSB);
|
||||
#endif
|
||||
#else
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct kvm_arch kvm_arm = {
|
||||
.ka_probe = _arm_probe,
|
||||
.ka_initvtop = _arm_initvtop,
|
||||
.ka_freevtop = _arm_freevtop,
|
||||
.ka_kvatop = _arm_kvatop,
|
||||
.ka_native = _arm_native,
|
||||
};
|
||||
|
||||
KVM_ARCH(kvm_arm);
|
||||
|
108
lib/libkvm/kvm_arm.h
Normal file
108
lib/libkvm/kvm_arm.h
Normal file
@ -0,0 +1,108 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 John H. Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __KVM_ARM_H__
|
||||
#define __KVM_ARM_H__
|
||||
|
||||
#ifdef __arm__
|
||||
#include <machine/pte.h>
|
||||
#endif
|
||||
|
||||
typedef uint32_t arm_physaddr_t;
|
||||
typedef uint32_t arm_pd_entry_t;
|
||||
typedef uint32_t arm_pt_entry_t;
|
||||
|
||||
#define ARM_PAGE_SHIFT 12
|
||||
#define ARM_PAGE_SIZE (1 << ARM_PAGE_SHIFT) /* Page size */
|
||||
#define ARM_PAGE_MASK (ARM_PAGE_SIZE - 1)
|
||||
|
||||
#define ARM_L1_TABLE_SIZE 0x4000 /* 16K */
|
||||
|
||||
#define ARM_L1_S_SIZE 0x00100000 /* 1M */
|
||||
#define ARM_L1_S_OFFSET (ARM_L1_S_SIZE - 1)
|
||||
#define ARM_L1_S_FRAME (~ARM_L1_S_OFFSET)
|
||||
#define ARM_L1_S_SHIFT 20
|
||||
|
||||
#define ARM_L2_L_SIZE 0x00010000 /* 64K */
|
||||
#define ARM_L2_L_OFFSET (ARM_L2_L_SIZE - 1)
|
||||
#define ARM_L2_L_FRAME (~ARM_L2_L_OFFSET)
|
||||
#define ARM_L2_L_SHIFT 16
|
||||
|
||||
#define ARM_L2_S_SIZE 0x00001000 /* 4K */
|
||||
#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_L1_TYPE_INV 0x00 /* Invalid (fault) */
|
||||
#define ARM_L1_TYPE_C 0x01 /* Coarse L2 */
|
||||
#define ARM_L1_TYPE_S 0x02 /* Section */
|
||||
#define ARM_L1_TYPE_MASK 0x03 /* Mask of type bits */
|
||||
|
||||
#define ARM_L1_S_ADDR_MASK 0xfff00000 /* phys address of section */
|
||||
#define ARM_L1_C_ADDR_MASK 0xfffffc00 /* phys address of L2 Table */
|
||||
|
||||
#define ARM_L2_TYPE_INV 0x00 /* Invalid (fault) */
|
||||
#define ARM_L2_TYPE_L 0x01 /* Large Page - 64k - not used yet*/
|
||||
#define ARM_L2_TYPE_S 0x02 /* Small Page - 4 */
|
||||
#define ARM_L2_TYPE_MASK 0x03
|
||||
|
||||
#define ARM_L2_ADDR_BITS 0x000ff000 /* L2 PTE address bits */
|
||||
|
||||
#ifdef __arm__
|
||||
_Static_assert(PAGE_SHIFT == ARM_PAGE_SHIFT, "PAGE_SHIFT mismatch");
|
||||
_Static_assert(PAGE_SIZE == ARM_PAGE_SIZE, "PAGE_SIZE mismatch");
|
||||
_Static_assert(PAGE_MASK == ARM_PAGE_MASK, "PAGE_MASK mismatch");
|
||||
_Static_assert(L1_TABLE_SIZE == ARM_L1_TABLE_SIZE, "L1_TABLE_SIZE mismatch");
|
||||
_Static_assert(L1_S_SIZE == ARM_L1_S_SIZE, "L1_S_SIZE mismatch");
|
||||
_Static_assert(L1_S_OFFSET == ARM_L1_S_OFFSET, "L1_S_OFFSET mismatch");
|
||||
_Static_assert(L1_S_FRAME == ARM_L1_S_FRAME, "L1_S_FRAME mismatch");
|
||||
_Static_assert(L1_S_SHIFT == ARM_L1_S_SHIFT, "L1_S_SHIFT mismatch");
|
||||
_Static_assert(L2_L_SIZE == ARM_L2_L_SIZE, "L2_L_SIZE mismatch");
|
||||
_Static_assert(L2_L_OFFSET == ARM_L2_L_OFFSET, "L2_L_OFFSET mismatch");
|
||||
_Static_assert(L2_L_FRAME == ARM_L2_L_FRAME, "L2_L_FRAME mismatch");
|
||||
_Static_assert(L2_L_SHIFT == ARM_L2_L_SHIFT, "L2_L_SHIFT mismatch");
|
||||
_Static_assert(L2_S_SIZE == ARM_L2_S_SIZE, "L2_S_SIZE mismatch");
|
||||
_Static_assert(L2_S_OFFSET == ARM_L2_S_OFFSET, "L2_S_OFFSET mismatch");
|
||||
_Static_assert(L2_S_FRAME == ARM_L2_S_FRAME, "L2_S_FRAME mismatch");
|
||||
_Static_assert(L2_S_SHIFT == ARM_L2_S_SHIFT, "L2_S_SHIFT mismatch");
|
||||
_Static_assert(L1_TYPE_INV == ARM_L1_TYPE_INV, "L1_TYPE_INV mismatch");
|
||||
_Static_assert(L1_TYPE_C == ARM_L1_TYPE_C, "L1_TYPE_C mismatch");
|
||||
_Static_assert(L1_TYPE_S == ARM_L1_TYPE_S, "L1_TYPE_S mismatch");
|
||||
_Static_assert(L1_TYPE_MASK == ARM_L1_TYPE_MASK, "L1_TYPE_MASK mismatch");
|
||||
_Static_assert(L1_S_ADDR_MASK == ARM_L1_S_ADDR_MASK, "L1_S_ADDR_MASK mismatch");
|
||||
_Static_assert(L1_C_ADDR_MASK == ARM_L1_C_ADDR_MASK, "L1_C_ADDR_MASK mismatch");
|
||||
_Static_assert(L2_TYPE_INV == ARM_L2_TYPE_INV, "L2_TYPE_INV mismatch");
|
||||
_Static_assert(L2_TYPE_L == ARM_L2_TYPE_L, "L2_TYPE_L mismatch");
|
||||
_Static_assert(L2_TYPE_S == ARM_L2_TYPE_S, "L2_TYPE_S mismatch");
|
||||
_Static_assert(L2_TYPE_MASK == ARM_L2_TYPE_MASK, "L2_TYPE_MASK mismatch");
|
||||
_Static_assert(L2_ADDR_BITS == ARM_L2_ADDR_BITS, "L2_ADDR_BITS mismatch");
|
||||
#endif
|
||||
|
||||
int _arm_native(kvm_t *);
|
||||
|
||||
#endif /* !__KVM_ARM_H__ */
|
@ -96,6 +96,12 @@ kvm_getcptime(kvm_t *kd, long *cp_time)
|
||||
return (getsysctl(kd, "kern.cp_time", cp_time, sizeof(long) *
|
||||
CPUSTATES));
|
||||
|
||||
if (!kd->arch->ka_native(kd)) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"cannot read cp_time from non-native core");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (kvm_cp_time_cached == 0) {
|
||||
if (_kvm_cp_time_init(kd) < 0)
|
||||
return (-1);
|
||||
|
@ -71,6 +71,12 @@ kvm_getloadavg(kvm_t *kd, double loadavg[], int nelem)
|
||||
if (ISALIVE(kd))
|
||||
return (getloadavg(loadavg, nelem));
|
||||
|
||||
if (!kd->arch->ka_native(kd)) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"cannot read loadavg from non-native core");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (kvm_nlist(kd, nl) != 0) {
|
||||
for (p = nl; p->n_type != 0; ++p);
|
||||
_kvm_err(kd, kd->program,
|
||||
|
@ -117,6 +117,12 @@ kvm_getswapinfo_kvm(kvm_t *kd, struct kvm_swap *swap_ary, int swap_max,
|
||||
struct swdevt *sp, swinfo;
|
||||
struct kvm_swap tot;
|
||||
|
||||
if (!kd->arch->ka_native(kd)) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"cannot read swapinfo from non-native core");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (!nlist_init(kd))
|
||||
return (-1);
|
||||
|
||||
|
@ -46,171 +46,133 @@ static char sccsid[] = "@(#)kvm_hp300.c 8.1 (Berkeley) 6/4/93";
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/endian.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <nlist.h>
|
||||
#include <kvm.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
|
||||
#include <machine/elf.h>
|
||||
#ifdef __i386__
|
||||
#include <machine/vmparam.h> /* For KERNBASE. */
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "kvm_private.h"
|
||||
#include "kvm_i386.h"
|
||||
|
||||
#ifndef btop
|
||||
#define btop(x) (i386_btop(x))
|
||||
#define ptob(x) (i386_ptob(x))
|
||||
#endif
|
||||
|
||||
#define PG_FRAME_PAE (~((uint64_t)PAGE_MASK))
|
||||
#define PDRSHIFT_PAE 21
|
||||
#define NPTEPG_PAE (PAGE_SIZE/sizeof(uint64_t))
|
||||
#define NBPDR_PAE (1<<PDRSHIFT_PAE)
|
||||
|
||||
/* minidump must be the first item! */
|
||||
struct vmstate {
|
||||
int minidump; /* 1 = minidump mode */
|
||||
void *mmapbase;
|
||||
size_t mmapsize;
|
||||
void *PTD;
|
||||
int pae;
|
||||
size_t phnum;
|
||||
GElf_Phdr *phdr;
|
||||
};
|
||||
|
||||
/*
|
||||
* Map the ELF headers into the process' address space. We do this in two
|
||||
* steps: first the ELF header itself and using that information the whole
|
||||
* set of headers.
|
||||
*/
|
||||
static int
|
||||
_kvm_maphdrs(kvm_t *kd, size_t sz)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
|
||||
/* munmap() previous mmap(). */
|
||||
if (vm->mmapbase != NULL) {
|
||||
munmap(vm->mmapbase, vm->mmapsize);
|
||||
vm->mmapbase = NULL;
|
||||
}
|
||||
|
||||
vm->mmapsize = sz;
|
||||
vm->mmapbase = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
||||
if (vm->mmapbase == MAP_FAILED) {
|
||||
_kvm_err(kd, kd->program, "cannot mmap corefile");
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate a physical memory address to a file-offset in the crash-dump.
|
||||
*/
|
||||
static size_t
|
||||
_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs)
|
||||
{
|
||||
Elf_Ehdr *e = kd->vmst->mmapbase;
|
||||
Elf_Phdr *p;
|
||||
int n;
|
||||
struct vmstate *vm = kd->vmst;
|
||||
GElf_Phdr *p;
|
||||
size_t n;
|
||||
|
||||
if (kd->rawdump) {
|
||||
*ofs = pa;
|
||||
return (PAGE_SIZE - ((size_t)pa & PAGE_MASK));
|
||||
return (I386_PAGE_SIZE - (pa & I386_PAGE_MASK));
|
||||
}
|
||||
|
||||
p = (Elf_Phdr*)((char*)e + e->e_phoff);
|
||||
n = e->e_phnum;
|
||||
p = vm->phdr;
|
||||
n = vm->phnum;
|
||||
while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
|
||||
p++, n--;
|
||||
if (n == 0)
|
||||
return (0);
|
||||
*ofs = (pa - p->p_paddr) + p->p_offset;
|
||||
return (PAGE_SIZE - ((size_t)pa & PAGE_MASK));
|
||||
return (I386_PAGE_SIZE - (pa & I386_PAGE_MASK));
|
||||
}
|
||||
|
||||
void
|
||||
_kvm_freevtop(kvm_t *kd)
|
||||
static void
|
||||
_i386_freevtop(kvm_t *kd)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
|
||||
if (kd->vmst->minidump)
|
||||
return (_kvm_minidump_freevtop(kd));
|
||||
if (vm->mmapbase != NULL)
|
||||
munmap(vm->mmapbase, vm->mmapsize);
|
||||
if (vm->PTD)
|
||||
free(vm->PTD);
|
||||
free(vm->phdr);
|
||||
free(vm);
|
||||
kd->vmst = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_initvtop(kvm_t *kd)
|
||||
static int
|
||||
_i386_probe(kvm_t *kd)
|
||||
{
|
||||
struct nlist nl[2];
|
||||
u_long pa;
|
||||
u_long kernbase;
|
||||
|
||||
return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_386) &&
|
||||
!_kvm_is_minidump(kd));
|
||||
}
|
||||
|
||||
static int
|
||||
_i386_initvtop(kvm_t *kd)
|
||||
{
|
||||
struct kvm_nlist nl[2];
|
||||
i386_physaddr_t pa;
|
||||
kvaddr_t kernbase;
|
||||
char *PTD;
|
||||
Elf_Ehdr *ehdr;
|
||||
size_t hdrsz;
|
||||
int i;
|
||||
char minihdr[8];
|
||||
|
||||
if (!kd->rawdump && pread(kd->pmfd, &minihdr, 8, 0) == 8)
|
||||
if (memcmp(&minihdr, "minidump", 8) == 0)
|
||||
return (_kvm_minidump_initvtop(kd));
|
||||
|
||||
kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
|
||||
if (kd->vmst == 0) {
|
||||
kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(struct vmstate));
|
||||
if (kd->vmst == NULL) {
|
||||
_kvm_err(kd, kd->program, "cannot allocate vm");
|
||||
return (-1);
|
||||
}
|
||||
kd->vmst->PTD = 0;
|
||||
|
||||
if (kd->rawdump == 0) {
|
||||
if (_kvm_maphdrs(kd, sizeof(Elf_Ehdr)) == -1)
|
||||
return (-1);
|
||||
|
||||
ehdr = kd->vmst->mmapbase;
|
||||
hdrsz = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum;
|
||||
if (_kvm_maphdrs(kd, hdrsz) == -1)
|
||||
if (_kvm_read_core_phdrs(kd, &kd->vmst->phnum,
|
||||
&kd->vmst->phdr) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
nl[0].n_name = "kernbase";
|
||||
nl[1].n_name = 0;
|
||||
|
||||
if (kvm_nlist(kd, nl) != 0)
|
||||
if (kvm_nlist2(kd, nl) != 0) {
|
||||
#ifdef __i386__
|
||||
kernbase = KERNBASE; /* for old kernels */
|
||||
else
|
||||
#else
|
||||
_kvm_err(kd, kd->program, "cannot resolve kernbase");
|
||||
return (-1);
|
||||
#endif
|
||||
} else
|
||||
kernbase = nl[0].n_value;
|
||||
|
||||
nl[0].n_name = "IdlePDPT";
|
||||
nl[1].n_name = 0;
|
||||
|
||||
if (kvm_nlist(kd, nl) == 0) {
|
||||
uint64_t pa64;
|
||||
if (kvm_nlist2(kd, nl) == 0) {
|
||||
i386_physaddr_pae_t pa64;
|
||||
|
||||
if (kvm_read(kd, (nl[0].n_value - kernbase), &pa,
|
||||
if (kvm_read2(kd, (nl[0].n_value - kernbase), &pa,
|
||||
sizeof(pa)) != sizeof(pa)) {
|
||||
_kvm_err(kd, kd->program, "cannot read IdlePDPT");
|
||||
return (-1);
|
||||
}
|
||||
PTD = _kvm_malloc(kd, 4 * PAGE_SIZE);
|
||||
pa = le32toh(pa);
|
||||
PTD = _kvm_malloc(kd, 4 * I386_PAGE_SIZE);
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (kvm_read(kd, pa + (i * sizeof(pa64)), &pa64,
|
||||
if (kvm_read2(kd, pa + (i * sizeof(pa64)), &pa64,
|
||||
sizeof(pa64)) != sizeof(pa64)) {
|
||||
_kvm_err(kd, kd->program, "Cannot read PDPT");
|
||||
free(PTD);
|
||||
return (-1);
|
||||
}
|
||||
if (kvm_read(kd, pa64 & PG_FRAME_PAE,
|
||||
PTD + (i * PAGE_SIZE), PAGE_SIZE) != (PAGE_SIZE)) {
|
||||
pa64 = le64toh(pa64);
|
||||
if (kvm_read2(kd, pa64 & I386_PG_FRAME_PAE,
|
||||
PTD + (i * I386_PAGE_SIZE), I386_PAGE_SIZE) !=
|
||||
I386_PAGE_SIZE) {
|
||||
_kvm_err(kd, kd->program, "cannot read PDPT");
|
||||
free(PTD);
|
||||
return (-1);
|
||||
@ -222,17 +184,18 @@ _kvm_initvtop(kvm_t *kd)
|
||||
nl[0].n_name = "IdlePTD";
|
||||
nl[1].n_name = 0;
|
||||
|
||||
if (kvm_nlist(kd, nl) != 0) {
|
||||
if (kvm_nlist2(kd, nl) != 0) {
|
||||
_kvm_err(kd, kd->program, "bad namelist");
|
||||
return (-1);
|
||||
}
|
||||
if (kvm_read(kd, (nl[0].n_value - kernbase), &pa,
|
||||
if (kvm_read2(kd, (nl[0].n_value - kernbase), &pa,
|
||||
sizeof(pa)) != sizeof(pa)) {
|
||||
_kvm_err(kd, kd->program, "cannot read IdlePTD");
|
||||
return (-1);
|
||||
}
|
||||
PTD = _kvm_malloc(kd, PAGE_SIZE);
|
||||
if (kvm_read(kd, pa, PTD, PAGE_SIZE) != PAGE_SIZE) {
|
||||
pa = le32toh(pa);
|
||||
PTD = _kvm_malloc(kd, I386_PAGE_SIZE);
|
||||
if (kvm_read2(kd, pa, PTD, I386_PAGE_SIZE) != I386_PAGE_SIZE) {
|
||||
_kvm_err(kd, kd->program, "cannot read PTD");
|
||||
return (-1);
|
||||
}
|
||||
@ -243,24 +206,23 @@ _kvm_initvtop(kvm_t *kd)
|
||||
}
|
||||
|
||||
static int
|
||||
_kvm_vatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
_i386_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
struct vmstate *vm;
|
||||
u_long offset;
|
||||
u_long pte_pa;
|
||||
u_long pde_pa;
|
||||
pd_entry_t pde;
|
||||
pt_entry_t pte;
|
||||
u_long pdeindex;
|
||||
u_long pteindex;
|
||||
i386_physaddr_t offset;
|
||||
i386_physaddr_t pte_pa;
|
||||
i386_pde_t pde;
|
||||
i386_pte_t pte;
|
||||
kvaddr_t pdeindex;
|
||||
kvaddr_t pteindex;
|
||||
size_t s;
|
||||
u_long a;
|
||||
i386_physaddr_t a;
|
||||
off_t ofs;
|
||||
uint32_t *PTD;
|
||||
i386_pde_t *PTD;
|
||||
|
||||
vm = kd->vmst;
|
||||
PTD = (uint32_t *)vm->PTD;
|
||||
offset = va & (PAGE_SIZE - 1);
|
||||
PTD = (i386_pde_t *)vm->PTD;
|
||||
offset = va & I386_PAGE_MASK;
|
||||
|
||||
/*
|
||||
* If we are initializing (kernel page table descriptor pointer
|
||||
@ -270,93 +232,87 @@ _kvm_vatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
s = _kvm_pa2off(kd, va, pa);
|
||||
if (s == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_kvm_vatop: bootstrap data not in dump");
|
||||
"_i386_vatop: bootstrap data not in dump");
|
||||
goto invalid;
|
||||
} else
|
||||
return (PAGE_SIZE - offset);
|
||||
return (I386_PAGE_SIZE - offset);
|
||||
}
|
||||
|
||||
pdeindex = va >> PDRSHIFT;
|
||||
pde = PTD[pdeindex];
|
||||
if (((u_long)pde & PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pde not valid");
|
||||
pdeindex = va >> I386_PDRSHIFT;
|
||||
pde = le32toh(PTD[pdeindex]);
|
||||
if ((pde & I386_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_i386_vatop: pde not valid");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if ((u_long)pde & PG_PS) {
|
||||
/*
|
||||
* No second-level page table; ptd describes one 4MB page.
|
||||
* (We assume that the kernel wouldn't set PG_PS without enabling
|
||||
* it cr0).
|
||||
*/
|
||||
#define PAGE4M_MASK (NBPDR - 1)
|
||||
#define PG_FRAME4M (~PAGE4M_MASK)
|
||||
pde_pa = ((u_long)pde & PG_FRAME4M) + (va & PAGE4M_MASK);
|
||||
s = _kvm_pa2off(kd, pde_pa, &ofs);
|
||||
if (pde & I386_PG_PS) {
|
||||
/*
|
||||
* No second-level page table; ptd describes one 4MB
|
||||
* page. (We assume that the kernel wouldn't set
|
||||
* PG_PS without enabling it cr0).
|
||||
*/
|
||||
offset = va & I386_PAGE_PS_MASK;
|
||||
a = (pde & I386_PG_PS_FRAME) + offset;
|
||||
s = _kvm_pa2off(kd, a, pa);
|
||||
if (s == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_kvm_vatop: 4MB page address not in dump");
|
||||
"_i386_vatop: 4MB page address not in dump");
|
||||
goto invalid;
|
||||
}
|
||||
*pa = ofs;
|
||||
return (NBPDR - (va & PAGE4M_MASK));
|
||||
return (I386_NBPDR - offset);
|
||||
}
|
||||
|
||||
pteindex = (va >> PAGE_SHIFT) & (NPTEPG-1);
|
||||
pte_pa = ((u_long)pde & PG_FRAME) + (pteindex * sizeof(pde));
|
||||
pteindex = (va >> I386_PAGE_SHIFT) & (I386_NPTEPG - 1);
|
||||
pte_pa = (pde & I386_PG_FRAME) + (pteindex * sizeof(pte));
|
||||
|
||||
s = _kvm_pa2off(kd, pte_pa, &ofs);
|
||||
if (s < sizeof pte) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pdpe_pa not found");
|
||||
if (s < sizeof(pte)) {
|
||||
_kvm_err(kd, kd->program, "_i386_vatop: pte_pa not found");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
/* XXX This has to be a physical address read, kvm_read is virtual */
|
||||
if (lseek(kd->pmfd, ofs, 0) == -1) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: lseek");
|
||||
if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
|
||||
_kvm_syserr(kd, kd->program, "_i386_vatop: pread");
|
||||
goto invalid;
|
||||
}
|
||||
if (read(kd->pmfd, &pte, sizeof pte) != sizeof pte) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: read");
|
||||
goto invalid;
|
||||
}
|
||||
if (((u_long)pte & PG_V) == 0) {
|
||||
pte = le32toh(pte);
|
||||
if ((pte & I386_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_kvatop: pte not valid");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
a = ((u_long)pte & PG_FRAME) + offset;
|
||||
s =_kvm_pa2off(kd, a, pa);
|
||||
a = (pte & I386_PG_FRAME) + offset;
|
||||
s = _kvm_pa2off(kd, a, pa);
|
||||
if (s == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: address not in dump");
|
||||
_kvm_err(kd, kd->program, "_i386_vatop: address not in dump");
|
||||
goto invalid;
|
||||
} else
|
||||
return (PAGE_SIZE - offset);
|
||||
return (I386_PAGE_SIZE - offset);
|
||||
|
||||
invalid:
|
||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
||||
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_kvm_vatop_pae(kvm_t *kd, u_long va, off_t *pa)
|
||||
_i386_vatop_pae(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
struct vmstate *vm;
|
||||
uint64_t offset;
|
||||
uint64_t pte_pa;
|
||||
uint64_t pde_pa;
|
||||
uint64_t pde;
|
||||
uint64_t pte;
|
||||
u_long pdeindex;
|
||||
u_long pteindex;
|
||||
i386_physaddr_pae_t offset;
|
||||
i386_physaddr_pae_t pte_pa;
|
||||
i386_pde_pae_t pde;
|
||||
i386_pte_pae_t pte;
|
||||
kvaddr_t pdeindex;
|
||||
kvaddr_t pteindex;
|
||||
size_t s;
|
||||
uint64_t a;
|
||||
i386_physaddr_pae_t a;
|
||||
off_t ofs;
|
||||
uint64_t *PTD;
|
||||
i386_pde_pae_t *PTD;
|
||||
|
||||
vm = kd->vmst;
|
||||
PTD = (uint64_t *)vm->PTD;
|
||||
offset = va & (PAGE_SIZE - 1);
|
||||
PTD = (i386_pde_pae_t *)vm->PTD;
|
||||
offset = va & I386_PAGE_MASK;
|
||||
|
||||
/*
|
||||
* If we are initializing (kernel page table descriptor pointer
|
||||
@ -366,87 +322,101 @@ _kvm_vatop_pae(kvm_t *kd, u_long va, off_t *pa)
|
||||
s = _kvm_pa2off(kd, va, pa);
|
||||
if (s == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_kvm_vatop_pae: bootstrap data not in dump");
|
||||
"_i386_vatop_pae: bootstrap data not in dump");
|
||||
goto invalid;
|
||||
} else
|
||||
return (PAGE_SIZE - offset);
|
||||
return (I386_PAGE_SIZE - offset);
|
||||
}
|
||||
|
||||
pdeindex = va >> PDRSHIFT_PAE;
|
||||
pde = PTD[pdeindex];
|
||||
if (((u_long)pde & PG_V) == 0) {
|
||||
pdeindex = va >> I386_PDRSHIFT_PAE;
|
||||
pde = le64toh(PTD[pdeindex]);
|
||||
if ((pde & I386_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_kvatop_pae: pde not valid");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if ((u_long)pde & PG_PS) {
|
||||
/*
|
||||
* No second-level page table; ptd describes one 2MB page.
|
||||
* (We assume that the kernel wouldn't set PG_PS without enabling
|
||||
* it cr0).
|
||||
*/
|
||||
#define PAGE2M_MASK (NBPDR_PAE - 1)
|
||||
#define PG_FRAME2M (~PAGE2M_MASK)
|
||||
pde_pa = ((u_long)pde & PG_FRAME2M) + (va & PAGE2M_MASK);
|
||||
s = _kvm_pa2off(kd, pde_pa, &ofs);
|
||||
if (pde & I386_PG_PS) {
|
||||
/*
|
||||
* No second-level page table; ptd describes one 2MB
|
||||
* page. (We assume that the kernel wouldn't set
|
||||
* PG_PS without enabling it cr0).
|
||||
*/
|
||||
offset = va & I386_PAGE_PS_MASK_PAE;
|
||||
a = (pde & I386_PG_PS_FRAME_PAE) + offset;
|
||||
s = _kvm_pa2off(kd, a, pa);
|
||||
if (s == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_kvm_vatop: 2MB page address not in dump");
|
||||
"_i386_vatop: 2MB page address not in dump");
|
||||
goto invalid;
|
||||
}
|
||||
*pa = ofs;
|
||||
return (NBPDR_PAE - (va & PAGE2M_MASK));
|
||||
return (I386_NBPDR_PAE - offset);
|
||||
}
|
||||
|
||||
pteindex = (va >> PAGE_SHIFT) & (NPTEPG_PAE-1);
|
||||
pte_pa = ((uint64_t)pde & PG_FRAME_PAE) + (pteindex * sizeof(pde));
|
||||
pteindex = (va >> I386_PAGE_SHIFT) & (I386_NPTEPG_PAE - 1);
|
||||
pte_pa = (pde & I386_PG_FRAME_PAE) + (pteindex * sizeof(pde));
|
||||
|
||||
s = _kvm_pa2off(kd, pte_pa, &ofs);
|
||||
if (s < sizeof pte) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop_pae: pdpe_pa not found");
|
||||
if (s < sizeof(pte)) {
|
||||
_kvm_err(kd, kd->program, "_i386_vatop_pae: pdpe_pa not found");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
/* XXX This has to be a physical address read, kvm_read is virtual */
|
||||
if (lseek(kd->pmfd, ofs, 0) == -1) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_vatop_pae: lseek");
|
||||
if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
|
||||
_kvm_syserr(kd, kd->program, "_i386_vatop_pae: read");
|
||||
goto invalid;
|
||||
}
|
||||
if (read(kd->pmfd, &pte, sizeof pte) != sizeof pte) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_vatop_pae: read");
|
||||
goto invalid;
|
||||
}
|
||||
if (((uint64_t)pte & PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop_pae: pte not valid");
|
||||
pte = le64toh(pte);
|
||||
if ((pte & I386_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_i386_vatop_pae: pte not valid");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
a = ((uint64_t)pte & PG_FRAME_PAE) + offset;
|
||||
s =_kvm_pa2off(kd, a, pa);
|
||||
a = (pte & I386_PG_FRAME_PAE) + offset;
|
||||
s = _kvm_pa2off(kd, a, pa);
|
||||
if (s == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_kvm_vatop_pae: address not in dump");
|
||||
"_i386_vatop_pae: address not in dump");
|
||||
goto invalid;
|
||||
} else
|
||||
return (PAGE_SIZE - offset);
|
||||
return (I386_PAGE_SIZE - offset);
|
||||
|
||||
invalid:
|
||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
||||
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
static int
|
||||
_i386_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
|
||||
if (kd->vmst->minidump)
|
||||
return (_kvm_minidump_kvatop(kd, va, pa));
|
||||
if (ISALIVE(kd)) {
|
||||
_kvm_err(kd, 0, "vatop called in live kernel!");
|
||||
return (0);
|
||||
}
|
||||
if (kd->vmst->pae)
|
||||
return (_kvm_vatop_pae(kd, va, pa));
|
||||
return (_i386_vatop_pae(kd, va, pa));
|
||||
else
|
||||
return (_kvm_vatop(kd, va, pa));
|
||||
return (_i386_vatop(kd, va, pa));
|
||||
}
|
||||
|
||||
int
|
||||
_i386_native(kvm_t *kd)
|
||||
{
|
||||
|
||||
#ifdef __i386__
|
||||
return (1);
|
||||
#else
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct kvm_arch kvm_i386 = {
|
||||
.ka_probe = _i386_probe,
|
||||
.ka_initvtop = _i386_initvtop,
|
||||
.ka_freevtop = _i386_freevtop,
|
||||
.ka_kvatop = _i386_kvatop,
|
||||
.ka_native = _i386_native,
|
||||
};
|
||||
|
||||
KVM_ARCH(kvm_i386);
|
||||
|
79
lib/libkvm/kvm_i386.h
Normal file
79
lib/libkvm/kvm_i386.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 John H. Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __KVM_I386_H__
|
||||
#define __KVM_I386_H__
|
||||
|
||||
#ifdef __i386__
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
#endif
|
||||
|
||||
typedef uint32_t i386_physaddr_t;
|
||||
typedef uint32_t i386_pte_t;
|
||||
typedef uint32_t i386_pde_t;
|
||||
typedef uint64_t i386_physaddr_pae_t;
|
||||
typedef uint64_t i386_pte_pae_t;
|
||||
typedef uint64_t i386_pde_pae_t;
|
||||
|
||||
#define I386_PAGE_SHIFT 12
|
||||
#define I386_PAGE_SIZE (1 << I386_PAGE_SHIFT)
|
||||
#define I386_PAGE_MASK (I386_PAGE_SIZE - 1)
|
||||
#define I386_NPTEPG (I386_PAGE_SIZE / sizeof(i386_pte_t))
|
||||
#define I386_PDRSHIFT 22
|
||||
#define I386_NBPDR (1 << I386_PDRSHIFT)
|
||||
#define I386_PAGE_PS_MASK (I386_NBPDR - 1)
|
||||
#define I386_NPTEPG_PAE (I386_PAGE_SIZE / sizeof(i386_pte_pae_t))
|
||||
#define I386_PDRSHIFT_PAE 21
|
||||
#define I386_NBPDR_PAE (1 << I386_PDRSHIFT_PAE)
|
||||
#define I386_PAGE_PS_MASK_PAE (I386_NBPDR_PAE - 1)
|
||||
|
||||
#define I386_PG_V 0x001
|
||||
#define I386_PG_PS 0x080
|
||||
#define I386_PG_FRAME_PAE (0x000ffffffffff000ull)
|
||||
#define I386_PG_PS_FRAME_PAE (0x000fffffffe00000ull)
|
||||
#define I386_PG_FRAME (0xfffff000)
|
||||
#define I386_PG_PS_FRAME (0xffc00000)
|
||||
|
||||
#ifdef __i386__
|
||||
_Static_assert(PAGE_SHIFT == I386_PAGE_SHIFT, "PAGE_SHIFT mismatch");
|
||||
_Static_assert(PAGE_SIZE == I386_PAGE_SIZE, "PAGE_SIZE mismatch");
|
||||
_Static_assert(PAGE_MASK == I386_PAGE_MASK, "PAGE_MASK mismatch");
|
||||
_Static_assert(NPTEPG == I386_NPTEPG, "NPTEPG mismatch");
|
||||
_Static_assert(PDRSHIFT == I386_PDRSHIFT, "PDRSHIFT mismatch");
|
||||
_Static_assert(NBPDR == I386_NBPDR, "NBPDR mismatch");
|
||||
|
||||
_Static_assert(PG_V == I386_PG_V, "PG_V mismatch");
|
||||
_Static_assert(PG_PS == I386_PG_PS, "PG_PS mismatch");
|
||||
_Static_assert(PG_FRAME == I386_PG_FRAME, "PG_FRAME mismatch");
|
||||
_Static_assert(PG_PS_FRAME == I386_PG_PS_FRAME, "PG_PS_FRAME mismatch");
|
||||
#endif
|
||||
|
||||
int _i386_native(kvm_t *);
|
||||
|
||||
#endif /* !__KVM_I386_H__ */
|
@ -33,112 +33,51 @@ __FBSDID("$FreeBSD$");
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/fnv_hash.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <nlist.h>
|
||||
#include <kvm.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
|
||||
#include <machine/elf.h>
|
||||
#include <machine/cpufunc.h>
|
||||
#include <machine/pte.h>
|
||||
#include <machine/minidump.h>
|
||||
#include "../../sys/arm64/include/minidump.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "kvm_private.h"
|
||||
#include "kvm_aarch64.h"
|
||||
|
||||
struct hpte {
|
||||
struct hpte *next;
|
||||
vm_paddr_t pa;
|
||||
int64_t off;
|
||||
};
|
||||
#define aarch64_round_page(x) roundup2((kvaddr_t)(x), AARCH64_PAGE_SIZE)
|
||||
|
||||
#define HPT_SIZE 1024
|
||||
|
||||
/* minidump must be the first item! */
|
||||
struct vmstate {
|
||||
int minidump; /* 1 = minidump mode */
|
||||
struct minidumphdr hdr;
|
||||
void *hpt_head[HPT_SIZE];
|
||||
uint64_t *bitmap;
|
||||
struct hpt hpt;
|
||||
uint64_t *page_map;
|
||||
};
|
||||
|
||||
static void
|
||||
hpt_insert(kvm_t *kd, vm_paddr_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, vm_paddr_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, uint64_t *base, int len, off_t off)
|
||||
_aarch64_minidump_probe(kvm_t *kd)
|
||||
{
|
||||
uint64_t idx;
|
||||
uint64_t bit, bits;
|
||||
vm_paddr_t pa;
|
||||
|
||||
for (idx = 0; idx < len / sizeof(*base); idx++) {
|
||||
bits = base[idx];
|
||||
while (bits) {
|
||||
bit = ffsl(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, ELFCLASS64, EM_AARCH64) &&
|
||||
_kvm_is_minidump(kd));
|
||||
}
|
||||
|
||||
void
|
||||
_kvm_minidump_freevtop(kvm_t *kd)
|
||||
static void
|
||||
_aarch64_minidump_freevtop(kvm_t *kd)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
|
||||
free(vm->bitmap);
|
||||
_kvm_hpt_free(&vm->hpt);
|
||||
free(vm->page_map);
|
||||
free(vm);
|
||||
kd->vmst = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_minidump_initvtop(kvm_t *kd)
|
||||
static int
|
||||
_aarch64_minidump_initvtop(kvm_t *kd)
|
||||
{
|
||||
struct vmstate *vmst;
|
||||
uint64_t *bitmap;
|
||||
off_t off;
|
||||
|
||||
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
||||
@ -147,7 +86,6 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||
return (-1);
|
||||
}
|
||||
kd->vmst = vmst;
|
||||
vmst->minidump = 1;
|
||||
if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
|
||||
sizeof(vmst->hdr)) {
|
||||
_kvm_err(kd, kd->program, "cannot read dump header");
|
||||
@ -159,111 +97,157 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (vmst->hdr.version != MINIDUMP_VERSION && vmst->hdr.version != 1) {
|
||||
vmst->hdr.version = le32toh(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 = le32toh(vmst->hdr.msgbufsize);
|
||||
vmst->hdr.bitmapsize = le32toh(vmst->hdr.bitmapsize);
|
||||
vmst->hdr.pmapsize = le32toh(vmst->hdr.pmapsize);
|
||||
vmst->hdr.kernbase = le64toh(vmst->hdr.kernbase);
|
||||
vmst->hdr.dmapphys = le64toh(vmst->hdr.dmapphys);
|
||||
vmst->hdr.dmapbase = le64toh(vmst->hdr.dmapbase);
|
||||
vmst->hdr.dmapend = le64toh(vmst->hdr.dmapend);
|
||||
|
||||
/* Skip header and msgbuf */
|
||||
off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
|
||||
off = AARCH64_PAGE_SIZE + aarch64_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) !=
|
||||
vmst->hdr.bitmapsize) {
|
||||
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 += 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);
|
||||
free(bitmap);
|
||||
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) <
|
||||
PAGE_SIZE) {
|
||||
AARCH64_PAGE_SIZE) {
|
||||
_kvm_err(kd, kd->program, "cannot read %d bytes for page_map",
|
||||
vmst->hdr.pmapsize);
|
||||
free(bitmap);
|
||||
return (-1);
|
||||
}
|
||||
off += vmst->hdr.pmapsize;
|
||||
|
||||
/* 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,
|
||||
AARCH64_PAGE_SIZE, sizeof(*bitmap));
|
||||
free(bitmap);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
_aarch64_minidump_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
struct vmstate *vm;
|
||||
u_long offset;
|
||||
pt_entry_t l3;
|
||||
u_long l3_index;
|
||||
u_long a;
|
||||
aarch64_physaddr_t offset;
|
||||
aarch64_pte_t l3;
|
||||
kvaddr_t l3_index;
|
||||
aarch64_physaddr_t a;
|
||||
off_t ofs;
|
||||
|
||||
vm = kd->vmst;
|
||||
offset = va & PAGE_MASK;
|
||||
offset = va & AARCH64_PAGE_MASK;
|
||||
|
||||
if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
|
||||
a = (va - vm->hdr.dmapbase + vm->hdr.dmapphys) & ~PAGE_MASK;
|
||||
ofs = hpt_find(kd, a);
|
||||
a = (va - vm->hdr.dmapbase + vm->hdr.dmapphys) &
|
||||
~AARCH64_PAGE_MASK;
|
||||
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||
if (ofs == -1) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: "
|
||||
"direct map address 0x%lx not in minidump", va);
|
||||
_kvm_err(kd, kd->program, "_aarch64_minidump_vatop: "
|
||||
"direct map address 0x%jx not in minidump",
|
||||
(uintmax_t)va);
|
||||
goto invalid;
|
||||
}
|
||||
*pa = ofs + offset;
|
||||
return (PAGE_SIZE - offset);
|
||||
return (AARCH64_PAGE_SIZE - offset);
|
||||
} else if (va >= vm->hdr.kernbase) {
|
||||
l3_index = (va - vm->hdr.kernbase) >> L3_SHIFT;
|
||||
l3_index = (va - vm->hdr.kernbase) >> AARCH64_L3_SHIFT;
|
||||
if (l3_index >= vm->hdr.pmapsize / sizeof(*vm->page_map))
|
||||
goto invalid;
|
||||
l3 = vm->page_map[l3_index];
|
||||
if ((l3 & ATTR_DESCR_MASK) != L3_PAGE) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pde not valid");
|
||||
l3 = le64toh(vm->page_map[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 & ~ATTR_MASK;
|
||||
ofs = hpt_find(kd, a);
|
||||
a = l3 & ~AARCH64_ATTR_MASK;
|
||||
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, "_aarch64_minidump_vatop: "
|
||||
"physical address 0x%jx not in minidump",
|
||||
(uintmax_t)a);
|
||||
goto invalid;
|
||||
}
|
||||
*pa = ofs + offset;
|
||||
return (PAGE_SIZE - offset);
|
||||
return (AARCH64_PAGE_SIZE - offset);
|
||||
} else {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_kvm_vatop: virtual address 0x%lx not minidumped", va);
|
||||
"_aarch64_minidump_vatop: virtual address 0x%jx not minidumped",
|
||||
(uintmax_t)va);
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
invalid:
|
||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
||||
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
static int
|
||||
_aarch64_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
|
||||
if (ISALIVE(kd)) {
|
||||
_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
|
||||
_kvm_err(kd, 0,
|
||||
"_aarch64_minidump_kvatop called in live kernel!");
|
||||
return (0);
|
||||
}
|
||||
return (_kvm_minidump_vatop(kd, va, pa));
|
||||
return (_aarch64_minidump_vatop(kd, va, pa));
|
||||
}
|
||||
|
||||
static int
|
||||
_aarch64_native(kvm_t *kd)
|
||||
{
|
||||
|
||||
#ifdef __aarch64__
|
||||
return (1);
|
||||
#else
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
KVM_ARCH(kvm_aarch64_minidump);
|
||||
|
@ -31,112 +31,53 @@ __FBSDID("$FreeBSD$");
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/fnv_hash.h>
|
||||
#include <sys/endian.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <nlist.h>
|
||||
#include <kvm.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
|
||||
#include <machine/elf.h>
|
||||
#include <machine/cpufunc.h>
|
||||
#include <machine/minidump.h>
|
||||
#include "../../sys/amd64/include/minidump.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "kvm_private.h"
|
||||
#include "kvm_amd64.h"
|
||||
|
||||
struct hpte {
|
||||
struct hpte *next;
|
||||
vm_paddr_t pa;
|
||||
int64_t off;
|
||||
};
|
||||
#define amd64_round_page(x) roundup2((kvaddr_t)(x), AMD64_PAGE_SIZE)
|
||||
|
||||
#define HPT_SIZE 1024
|
||||
|
||||
/* minidump must be the first item! */
|
||||
struct vmstate {
|
||||
int minidump; /* 1 = minidump mode */
|
||||
struct minidumphdr hdr;
|
||||
void *hpt_head[HPT_SIZE];
|
||||
uint64_t *bitmap;
|
||||
uint64_t *page_map;
|
||||
struct hpt hpt;
|
||||
amd64_pte_t *page_map;
|
||||
};
|
||||
|
||||
static void
|
||||
hpt_insert(kvm_t *kd, vm_paddr_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, vm_paddr_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, uint64_t *base, int len, off_t off)
|
||||
_amd64_minidump_probe(kvm_t *kd)
|
||||
{
|
||||
uint64_t idx;
|
||||
uint64_t bit, bits;
|
||||
vm_paddr_t pa;
|
||||
|
||||
for (idx = 0; idx < len / sizeof(*base); idx++) {
|
||||
bits = base[idx];
|
||||
while (bits) {
|
||||
bit = bsfq(bits);
|
||||
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, ELFCLASS64, EM_X86_64) &&
|
||||
_kvm_is_minidump(kd));
|
||||
}
|
||||
|
||||
void
|
||||
_kvm_minidump_freevtop(kvm_t *kd)
|
||||
static void
|
||||
_amd64_minidump_freevtop(kvm_t *kd)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
|
||||
if (vm->bitmap)
|
||||
free(vm->bitmap);
|
||||
_kvm_hpt_free(&vm->hpt);
|
||||
if (vm->page_map)
|
||||
free(vm->page_map);
|
||||
free(vm);
|
||||
kd->vmst = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_minidump_initvtop(kvm_t *kd)
|
||||
static int
|
||||
_amd64_minidump_initvtop(kvm_t *kd)
|
||||
{
|
||||
struct vmstate *vmst;
|
||||
uint64_t *bitmap;
|
||||
off_t off;
|
||||
|
||||
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
||||
@ -145,7 +86,6 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||
return (-1);
|
||||
}
|
||||
kd->vmst = vmst;
|
||||
vmst->minidump = 1;
|
||||
if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
|
||||
sizeof(vmst->hdr)) {
|
||||
_kvm_err(kd, kd->program, "cannot read dump header");
|
||||
@ -160,177 +100,222 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||
* NB: amd64 minidump header is binary compatible between version 1
|
||||
* and version 2; this may not be the case for the future versions.
|
||||
*/
|
||||
vmst->hdr.version = le32toh(vmst->hdr.version);
|
||||
if (vmst->hdr.version != MINIDUMP_VERSION && vmst->hdr.version != 1) {
|
||||
_kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d",
|
||||
MINIDUMP_VERSION, vmst->hdr.version);
|
||||
return (-1);
|
||||
}
|
||||
vmst->hdr.msgbufsize = le32toh(vmst->hdr.msgbufsize);
|
||||
vmst->hdr.bitmapsize = le32toh(vmst->hdr.bitmapsize);
|
||||
vmst->hdr.pmapsize = le32toh(vmst->hdr.pmapsize);
|
||||
vmst->hdr.kernbase = le64toh(vmst->hdr.kernbase);
|
||||
vmst->hdr.dmapbase = le64toh(vmst->hdr.dmapbase);
|
||||
vmst->hdr.dmapend = le64toh(vmst->hdr.dmapend);
|
||||
|
||||
/* Skip header and msgbuf */
|
||||
off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
|
||||
off = AMD64_PAGE_SIZE + amd64_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) !=
|
||||
vmst->hdr.bitmapsize) {
|
||||
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 += 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);
|
||||
free(bitmap);
|
||||
return (-1);
|
||||
}
|
||||
if (pread(kd->pmfd, vmst->page_map, vmst->hdr.pmapsize, off) !=
|
||||
vmst->hdr.pmapsize) {
|
||||
(ssize_t)vmst->hdr.pmapsize) {
|
||||
_kvm_err(kd, kd->program, "cannot read %d bytes for page_map", vmst->hdr.pmapsize);
|
||||
free(bitmap);
|
||||
return (-1);
|
||||
}
|
||||
off += vmst->hdr.pmapsize;
|
||||
|
||||
/* 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,
|
||||
AMD64_PAGE_SIZE, sizeof(*bitmap));
|
||||
free(bitmap);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_kvm_minidump_vatop_v1(kvm_t *kd, u_long va, off_t *pa)
|
||||
_amd64_minidump_vatop_v1(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
struct vmstate *vm;
|
||||
u_long offset;
|
||||
pt_entry_t pte;
|
||||
u_long pteindex;
|
||||
u_long a;
|
||||
amd64_physaddr_t offset;
|
||||
amd64_pte_t pte;
|
||||
kvaddr_t pteindex;
|
||||
amd64_physaddr_t a;
|
||||
off_t ofs;
|
||||
|
||||
vm = kd->vmst;
|
||||
offset = va & (PAGE_SIZE - 1);
|
||||
offset = va & AMD64_PAGE_MASK;
|
||||
|
||||
if (va >= vm->hdr.kernbase) {
|
||||
pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
|
||||
pteindex = (va - vm->hdr.kernbase) >> AMD64_PAGE_SHIFT;
|
||||
if (pteindex >= vm->hdr.pmapsize / sizeof(*vm->page_map))
|
||||
goto invalid;
|
||||
pte = vm->page_map[pteindex];
|
||||
if (((u_long)pte & PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
|
||||
pte = le64toh(vm->page_map[pteindex]);
|
||||
if ((pte & AMD64_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_amd64_minidump_vatop_v1: pte not valid");
|
||||
goto invalid;
|
||||
}
|
||||
a = pte & PG_FRAME;
|
||||
ofs = hpt_find(kd, a);
|
||||
a = pte & AMD64_PG_FRAME;
|
||||
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,
|
||||
"_amd64_minidump_vatop_v1: physical address 0x%jx not in minidump",
|
||||
(uintmax_t)a);
|
||||
goto invalid;
|
||||
}
|
||||
*pa = ofs + offset;
|
||||
return (PAGE_SIZE - offset);
|
||||
return (AMD64_PAGE_SIZE - offset);
|
||||
} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
|
||||
a = (va - vm->hdr.dmapbase) & ~PAGE_MASK;
|
||||
ofs = hpt_find(kd, a);
|
||||
a = (va - vm->hdr.dmapbase) & ~AMD64_PAGE_MASK;
|
||||
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||
if (ofs == -1) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: direct map address 0x%lx not in minidump", va);
|
||||
_kvm_err(kd, kd->program,
|
||||
"_amd64_minidump_vatop_v1: direct map address 0x%jx not in minidump",
|
||||
(uintmax_t)va);
|
||||
goto invalid;
|
||||
}
|
||||
*pa = ofs + offset;
|
||||
return (PAGE_SIZE - offset);
|
||||
return (AMD64_PAGE_SIZE - offset);
|
||||
} else {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
|
||||
_kvm_err(kd, kd->program,
|
||||
"_amd64_minidump_vatop_v1: virtual address 0x%jx not minidumped",
|
||||
(uintmax_t)va);
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
invalid:
|
||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
||||
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
_amd64_minidump_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
pt_entry_t pt[NPTEPG];
|
||||
amd64_pte_t pt[AMD64_NPTEPG];
|
||||
struct vmstate *vm;
|
||||
u_long offset;
|
||||
pd_entry_t pde;
|
||||
pd_entry_t pte;
|
||||
u_long pteindex;
|
||||
u_long pdeindex;
|
||||
u_long a;
|
||||
amd64_physaddr_t offset;
|
||||
amd64_pde_t pde;
|
||||
amd64_pte_t pte;
|
||||
kvaddr_t pteindex;
|
||||
kvaddr_t pdeindex;
|
||||
amd64_physaddr_t a;
|
||||
off_t ofs;
|
||||
|
||||
vm = kd->vmst;
|
||||
offset = va & PAGE_MASK;
|
||||
offset = va & AMD64_PAGE_MASK;
|
||||
|
||||
if (va >= vm->hdr.kernbase) {
|
||||
pdeindex = (va - vm->hdr.kernbase) >> PDRSHIFT;
|
||||
pdeindex = (va - vm->hdr.kernbase) >> AMD64_PDRSHIFT;
|
||||
if (pdeindex >= vm->hdr.pmapsize / sizeof(*vm->page_map))
|
||||
goto invalid;
|
||||
pde = vm->page_map[pdeindex];
|
||||
if (((u_long)pde & PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pde not valid");
|
||||
pde = le64toh(vm->page_map[pdeindex]);
|
||||
if ((pde & AMD64_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_amd64_minidump_vatop: pde not valid");
|
||||
goto invalid;
|
||||
}
|
||||
if ((pde & PG_PS) == 0) {
|
||||
a = pde & PG_FRAME;
|
||||
ofs = hpt_find(kd, a);
|
||||
if ((pde & AMD64_PG_PS) == 0) {
|
||||
a = pde & AMD64_PG_FRAME;
|
||||
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||
if (ofs == -1) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pt physical address 0x%lx not in minidump", a);
|
||||
_kvm_err(kd, kd->program,
|
||||
"_amd64_minidump_vatop: pt physical address 0x%jx not in minidump",
|
||||
(uintmax_t)a);
|
||||
goto invalid;
|
||||
}
|
||||
if (pread(kd->pmfd, &pt, PAGE_SIZE, ofs) != PAGE_SIZE) {
|
||||
_kvm_err(kd, kd->program, "cannot read %d bytes for pt", PAGE_SIZE);
|
||||
/* TODO: Just read the single PTE */
|
||||
if (pread(kd->pmfd, &pt, AMD64_PAGE_SIZE, ofs) !=
|
||||
AMD64_PAGE_SIZE) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"cannot read %d bytes for page table",
|
||||
AMD64_PAGE_SIZE);
|
||||
return (-1);
|
||||
}
|
||||
pteindex = (va >> PAGE_SHIFT) & ((1ul << NPTEPGSHIFT) - 1);
|
||||
pte = pt[pteindex];
|
||||
if (((u_long)pte & PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
|
||||
pteindex = (va >> AMD64_PAGE_SHIFT) &
|
||||
(AMD64_NPTEPG - 1);
|
||||
pte = le64toh(pt[pteindex]);
|
||||
if ((pte & AMD64_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_amd64_minidump_vatop: pte not valid");
|
||||
goto invalid;
|
||||
}
|
||||
a = pte & PG_FRAME;
|
||||
a = pte & AMD64_PG_FRAME;
|
||||
} else {
|
||||
a = pde & PG_PS_FRAME;
|
||||
a += (va & PDRMASK) ^ offset;
|
||||
a = pde & AMD64_PG_PS_FRAME;
|
||||
a += (va & AMD64_PDRMASK) ^ offset;
|
||||
}
|
||||
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,
|
||||
"_amd64_minidump_vatop: physical address 0x%jx not in minidump",
|
||||
(uintmax_t)a);
|
||||
goto invalid;
|
||||
}
|
||||
*pa = ofs + offset;
|
||||
return (PAGE_SIZE - offset);
|
||||
return (AMD64_PAGE_SIZE - offset);
|
||||
} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
|
||||
a = (va - vm->hdr.dmapbase) & ~PAGE_MASK;
|
||||
ofs = hpt_find(kd, a);
|
||||
a = (va - vm->hdr.dmapbase) & ~AMD64_PAGE_MASK;
|
||||
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||
if (ofs == -1) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: direct map address 0x%lx not in minidump", va);
|
||||
_kvm_err(kd, kd->program,
|
||||
"_amd64_minidump_vatop: direct map address 0x%jx not in minidump",
|
||||
(uintmax_t)va);
|
||||
goto invalid;
|
||||
}
|
||||
*pa = ofs + offset;
|
||||
return (PAGE_SIZE - offset);
|
||||
return (AMD64_PAGE_SIZE - offset);
|
||||
} else {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
|
||||
_kvm_err(kd, kd->program,
|
||||
"_amd64_minidump_vatop: virtual address 0x%jx not minidumped",
|
||||
(uintmax_t)va);
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
invalid:
|
||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
||||
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
static int
|
||||
_amd64_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
|
||||
if (ISALIVE(kd)) {
|
||||
_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
|
||||
_kvm_err(kd, 0,
|
||||
"_amd64_minidump_kvatop called in live kernel!");
|
||||
return (0);
|
||||
}
|
||||
if (((struct vmstate *)kd->vmst)->hdr.version == 1)
|
||||
return (_kvm_minidump_vatop_v1(kd, va, pa));
|
||||
return (_amd64_minidump_vatop_v1(kd, va, pa));
|
||||
else
|
||||
return (_kvm_minidump_vatop(kd, va, pa));
|
||||
return (_amd64_minidump_vatop(kd, va, pa));
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
KVM_ARCH(kvm_amd64_minidump);
|
||||
|
@ -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);
|
||||
|
@ -27,118 +27,57 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
/*
|
||||
* AMD64 machine dependent routines for kvm and minidumps.
|
||||
* i386 machine dependent routines for kvm and minidumps.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/fnv_hash.h>
|
||||
#include <sys/endian.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <nlist.h>
|
||||
#include <kvm.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
|
||||
#include <machine/elf.h>
|
||||
#include <machine/cpufunc.h>
|
||||
#include <machine/minidump.h>
|
||||
#include "../../sys/i386/include/minidump.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "kvm_private.h"
|
||||
#include "kvm_i386.h"
|
||||
|
||||
#define PG_FRAME_PAE (~((uint64_t)PAGE_MASK))
|
||||
#define i386_round_page(x) roundup2((kvaddr_t)(x), I386_PAGE_SIZE)
|
||||
|
||||
struct hpte {
|
||||
struct hpte *next;
|
||||
uint64_t pa;
|
||||
int64_t off;
|
||||
};
|
||||
|
||||
#define HPT_SIZE 1024
|
||||
|
||||
/* minidump must be the first item! */
|
||||
struct vmstate {
|
||||
int minidump; /* 1 = minidump mode */
|
||||
struct minidumphdr hdr;
|
||||
void *hpt_head[HPT_SIZE];
|
||||
uint32_t *bitmap;
|
||||
struct hpt hpt;
|
||||
void *ptemap;
|
||||
};
|
||||
|
||||
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)
|
||||
_i386_minidump_probe(kvm_t *kd)
|
||||
{
|
||||
uint64_t idx;
|
||||
uint32_t bit, bits;
|
||||
uint64_t pa;
|
||||
|
||||
for (idx = 0; idx < len / sizeof(*base); idx++) {
|
||||
bits = base[idx];
|
||||
while (bits) {
|
||||
bit = bsfl(bits);
|
||||
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_386) &&
|
||||
_kvm_is_minidump(kd));
|
||||
}
|
||||
|
||||
void
|
||||
_kvm_minidump_freevtop(kvm_t *kd)
|
||||
static void
|
||||
_i386_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
|
||||
_i386_minidump_initvtop(kvm_t *kd)
|
||||
{
|
||||
struct vmstate *vmst;
|
||||
uint32_t *bitmap;
|
||||
off_t off;
|
||||
|
||||
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
||||
@ -147,7 +86,6 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||
return (-1);
|
||||
}
|
||||
kd->vmst = vmst;
|
||||
vmst->minidump = 1;
|
||||
if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
|
||||
sizeof(vmst->hdr)) {
|
||||
_kvm_err(kd, kd->program, "cannot read dump header");
|
||||
@ -157,135 +95,166 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||
_kvm_err(kd, kd->program, "not a minidump for this platform");
|
||||
return (-1);
|
||||
}
|
||||
vmst->hdr.version = le32toh(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 = le32toh(vmst->hdr.msgbufsize);
|
||||
vmst->hdr.bitmapsize = le32toh(vmst->hdr.bitmapsize);
|
||||
vmst->hdr.ptesize = le32toh(vmst->hdr.ptesize);
|
||||
vmst->hdr.kernbase = le32toh(vmst->hdr.kernbase);
|
||||
vmst->hdr.paemode = le32toh(vmst->hdr.paemode);
|
||||
|
||||
/* Skip header and msgbuf */
|
||||
off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
|
||||
off = I386_PAGE_SIZE + i386_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 += 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);
|
||||
free(bitmap);
|
||||
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);
|
||||
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,
|
||||
I386_PAGE_SIZE, sizeof(*bitmap));
|
||||
free(bitmap);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_kvm_minidump_vatop_pae(kvm_t *kd, u_long va, off_t *pa)
|
||||
_i386_minidump_vatop_pae(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
struct vmstate *vm;
|
||||
uint64_t offset;
|
||||
uint64_t pte;
|
||||
u_long pteindex;
|
||||
uint64_t a;
|
||||
i386_physaddr_pae_t offset;
|
||||
i386_pte_pae_t pte;
|
||||
kvaddr_t pteindex;
|
||||
i386_physaddr_pae_t a;
|
||||
off_t ofs;
|
||||
uint64_t *ptemap;
|
||||
i386_pte_pae_t *ptemap;
|
||||
|
||||
vm = kd->vmst;
|
||||
ptemap = vm->ptemap;
|
||||
offset = va & (PAGE_SIZE - 1);
|
||||
offset = va & I386_PAGE_MASK;
|
||||
|
||||
if (va >= vm->hdr.kernbase) {
|
||||
pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
|
||||
pte = ptemap[pteindex];
|
||||
if ((pte & PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
|
||||
pteindex = (va - vm->hdr.kernbase) >> I386_PAGE_SHIFT;
|
||||
pte = le64toh(ptemap[pteindex]);
|
||||
if ((pte & I386_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_i386_minidump_vatop_pae: pte not valid");
|
||||
goto invalid;
|
||||
}
|
||||
a = pte & PG_FRAME_PAE;
|
||||
ofs = hpt_find(kd, a);
|
||||
a = pte & I386_PG_FRAME_PAE;
|
||||
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||
if (ofs == -1) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: physical address 0x%llx not in minidump", a);
|
||||
_kvm_err(kd, kd->program,
|
||||
"_i386_minidump_vatop_pae: physical address 0x%jx not in minidump",
|
||||
(uintmax_t)a);
|
||||
goto invalid;
|
||||
}
|
||||
*pa = ofs + offset;
|
||||
return (PAGE_SIZE - offset);
|
||||
return (I386_PAGE_SIZE - offset);
|
||||
} else {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
|
||||
_kvm_err(kd, kd->program,
|
||||
"_i386_minidump_vatop_pae: virtual address 0x%jx not minidumped",
|
||||
(uintmax_t)va);
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
invalid:
|
||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
||||
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_kvm_minidump_vatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
_i386_minidump_vatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
struct vmstate *vm;
|
||||
u_long offset;
|
||||
pt_entry_t pte;
|
||||
u_long pteindex;
|
||||
u_long a;
|
||||
i386_physaddr_t offset;
|
||||
i386_pte_t pte;
|
||||
kvaddr_t pteindex;
|
||||
i386_physaddr_t a;
|
||||
off_t ofs;
|
||||
uint32_t *ptemap;
|
||||
i386_pte_t *ptemap;
|
||||
|
||||
vm = kd->vmst;
|
||||
ptemap = vm->ptemap;
|
||||
offset = va & (PAGE_SIZE - 1);
|
||||
offset = va & I386_PAGE_MASK;
|
||||
|
||||
if (va >= vm->hdr.kernbase) {
|
||||
pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
|
||||
pte = ptemap[pteindex];
|
||||
if ((pte & PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
|
||||
pteindex = (va - vm->hdr.kernbase) >> I386_PAGE_SHIFT;
|
||||
pte = le32toh(ptemap[pteindex]);
|
||||
if ((pte & I386_PG_V) == 0) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"_i386_minidump_vatop: pte not valid");
|
||||
goto invalid;
|
||||
}
|
||||
a = pte & PG_FRAME;
|
||||
ofs = hpt_find(kd, a);
|
||||
a = pte & I386_PG_FRAME;
|
||||
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,
|
||||
"_i386_minidump_vatop: physical address 0x%jx not in minidump",
|
||||
(uintmax_t)a);
|
||||
goto invalid;
|
||||
}
|
||||
*pa = ofs + offset;
|
||||
return (PAGE_SIZE - offset);
|
||||
return (I386_PAGE_SIZE - offset);
|
||||
} else {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx not minidumped", va);
|
||||
_kvm_err(kd, kd->program,
|
||||
"_i386_minidump_vatop: virtual address 0x%jx not minidumped",
|
||||
(uintmax_t)va);
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
invalid:
|
||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
||||
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
static int
|
||||
_i386_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
|
||||
if (ISALIVE(kd)) {
|
||||
_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
|
||||
_kvm_err(kd, 0, "_i386_minidump_kvatop called in live kernel!");
|
||||
return (0);
|
||||
}
|
||||
if (kd->vmst->hdr.paemode)
|
||||
return (_kvm_minidump_vatop_pae(kd, va, pa));
|
||||
return (_i386_minidump_vatop_pae(kd, va, pa));
|
||||
else
|
||||
return (_kvm_minidump_vatop(kd, va, pa));
|
||||
return (_i386_minidump_vatop(kd, va, pa));
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
KVM_ARCH(kvm_i386_minidump);
|
||||
|
@ -35,112 +35,57 @@ __FBSDID("$FreeBSD$");
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/user.h>
|
||||
#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>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
|
||||
#include <machine/elf.h>
|
||||
#include <machine/cpufunc.h>
|
||||
#include <machine/minidump.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include "../../sys/mips/include/cpuregs.h"
|
||||
#include "../../sys/mips/include/minidump.h"
|
||||
|
||||
#include "kvm_private.h"
|
||||
#include "kvm_mips.h"
|
||||
|
||||
struct hpte {
|
||||
struct hpte *next;
|
||||
uint64_t pa;
|
||||
int64_t off;
|
||||
};
|
||||
#define mips_round_page(x) roundup2((kvaddr_t)(x), MIPS_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;
|
||||
int pte_size;
|
||||
};
|
||||
|
||||
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)
|
||||
_mips_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);
|
||||
if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32 &&
|
||||
kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64)
|
||||
return (0);
|
||||
if (kd->nlehdr.e_machine != EM_MIPS)
|
||||
return (0);
|
||||
return (_kvm_is_minidump(kd));
|
||||
}
|
||||
|
||||
void
|
||||
_kvm_minidump_freevtop(kvm_t *kd)
|
||||
static void
|
||||
_mips_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
|
||||
_mips_minidump_initvtop(kvm_t *kd)
|
||||
{
|
||||
struct vmstate *vmst;
|
||||
uint32_t *bitmap;
|
||||
off_t off;
|
||||
|
||||
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
||||
@ -150,9 +95,13 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||
}
|
||||
|
||||
kd->vmst = vmst;
|
||||
vmst->minidump = 1;
|
||||
|
||||
off = lseek(kd->pmfd, 0, SEEK_CUR);
|
||||
if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64 ||
|
||||
kd->nlehdr.e_flags & EF_MIPS_ABI2)
|
||||
vmst->pte_size = 64;
|
||||
else
|
||||
vmst->pte_size = 32;
|
||||
|
||||
if (pread(kd->pmfd, &vmst->hdr,
|
||||
sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
|
||||
_kvm_err(kd, kd->program, "cannot read dump header");
|
||||
@ -164,34 +113,43 @@ _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 = _kvm64toh(kd, vmst->hdr.kernbase);
|
||||
vmst->hdr.dmapbase = _kvm64toh(kd, vmst->hdr.dmapbase);
|
||||
vmst->hdr.dmapend = _kvm64toh(kd, vmst->hdr.dmapend);
|
||||
|
||||
/* Skip header and msgbuf */
|
||||
off = PAGE_SIZE + round_page(vmst->hdr.msgbufsize);
|
||||
off = MIPS_PAGE_SIZE + mips_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 += 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);
|
||||
free(bitmap);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
@ -199,75 +157,139 @@ _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,
|
||||
MIPS_PAGE_SIZE, sizeof(*bitmap));
|
||||
free(bitmap);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
static int
|
||||
_mips_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
struct vmstate *vm;
|
||||
pt_entry_t pte;
|
||||
u_long offset, pteindex, a;
|
||||
uint64_t pte;
|
||||
mips_physaddr_t offset, a;
|
||||
kvaddr_t pteindex;
|
||||
off_t ofs;
|
||||
pt_entry_t *ptemap;
|
||||
uint32_t *ptemap32;
|
||||
uint64_t *ptemap64;
|
||||
|
||||
if (ISALIVE(kd)) {
|
||||
_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
|
||||
_kvm_err(kd, 0, "_mips_minidump_kvatop called in live kernel!");
|
||||
return (0);
|
||||
}
|
||||
|
||||
offset = va & PAGE_MASK;
|
||||
offset = va & MIPS_PAGE_MASK;
|
||||
/* Operate with page-aligned address */
|
||||
va &= ~PAGE_MASK;
|
||||
va &= ~MIPS_PAGE_MASK;
|
||||
|
||||
vm = kd->vmst;
|
||||
ptemap = vm->ptemap;
|
||||
ptemap32 = vm->ptemap;
|
||||
ptemap64 = vm->ptemap;
|
||||
|
||||
#if defined(__mips_n64)
|
||||
if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END)
|
||||
a = (MIPS_XKPHYS_TO_PHYS(va));
|
||||
else
|
||||
#endif
|
||||
if (va >= (u_long)MIPS_KSEG0_START && va < (u_long)MIPS_KSEG0_END)
|
||||
a = (MIPS_KSEG0_TO_PHYS(va));
|
||||
else if (va >= (u_long)MIPS_KSEG1_START && va < (u_long)MIPS_KSEG1_END)
|
||||
a = (MIPS_KSEG1_TO_PHYS(va));
|
||||
else if (va >= vm->hdr.kernbase) {
|
||||
pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
|
||||
pte = ptemap[pteindex];
|
||||
if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64) {
|
||||
if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END) {
|
||||
a = va & MIPS_XKPHYS_PHYS_MASK;
|
||||
goto found;
|
||||
}
|
||||
if (va >= MIPS64_KSEG0_START && va < MIPS64_KSEG0_END) {
|
||||
a = va & MIPS_KSEG0_PHYS_MASK;
|
||||
goto found;
|
||||
}
|
||||
if (va >= MIPS64_KSEG1_START && va < MIPS64_KSEG1_END) {
|
||||
a = va & MIPS_KSEG0_PHYS_MASK;
|
||||
goto found;
|
||||
}
|
||||
} else {
|
||||
if (va >= MIPS32_KSEG0_START && va < MIPS32_KSEG0_END) {
|
||||
a = va & MIPS_KSEG0_PHYS_MASK;
|
||||
goto found;
|
||||
}
|
||||
if (va >= MIPS32_KSEG1_START && va < MIPS32_KSEG1_END) {
|
||||
a = va & MIPS_KSEG0_PHYS_MASK;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
if (va >= vm->hdr.kernbase) {
|
||||
pteindex = (va - vm->hdr.kernbase) >> MIPS_PAGE_SHIFT;
|
||||
if (vm->pte_size == 64) {
|
||||
pte = _kvm64toh(kd, ptemap64[pteindex]);
|
||||
a = MIPS64_PTE_TO_PA(pte);
|
||||
} else {
|
||||
pte = _kvm32toh(kd, ptemap32[pteindex]);
|
||||
a = MIPS32_PTE_TO_PA(pte);
|
||||
}
|
||||
if (!pte) {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
|
||||
_kvm_err(kd, kd->program, "_mips_minidump_kvatop: pte "
|
||||
"not valid");
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
a = TLBLO_PTE_TO_PA(pte);
|
||||
|
||||
} else {
|
||||
_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx "
|
||||
"not minidumped", va);
|
||||
_kvm_err(kd, kd->program, "_mips_minidump_kvatop: virtual "
|
||||
"address 0x%jx not minidumped", (uintmax_t)va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
ofs = hpt_find(kd, a);
|
||||
found:
|
||||
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, "_mips_minidump_kvatop: physical "
|
||||
"address 0x%jx not in minidump", (uintmax_t)a);
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
*pa = ofs + offset;
|
||||
return (PAGE_SIZE - offset);
|
||||
return (MIPS_PAGE_SIZE - offset);
|
||||
|
||||
|
||||
invalid:
|
||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
||||
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_mips_native(kvm_t *kd)
|
||||
{
|
||||
|
||||
#ifdef __mips__
|
||||
#ifdef __mips_n64
|
||||
if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64)
|
||||
return (0);
|
||||
#else
|
||||
if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32)
|
||||
return (0);
|
||||
#ifdef __mips_n32
|
||||
if (!(kd->nlehdr.e_flags & EF_MIPS_ABI2))
|
||||
return (0);
|
||||
#else
|
||||
if (kd->nlehdr.e_flags & EF_MIPS_ABI2)
|
||||
return (0);
|
||||
#endif
|
||||
#endif
|
||||
#if _BYTE_ORDER == _LITTLE_ENDIAN
|
||||
return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2LSB);
|
||||
#else
|
||||
return (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2MSB);
|
||||
#endif
|
||||
#else
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
KVM_ARCH(kvm_mips_minidump);
|
||||
|
@ -1,116 +0,0 @@
|
||||
/*-
|
||||
* Copyright (C) 2006 Bruce M. Simpson.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* MIPS machine dependent routines for kvm.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/elf32.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <machine/pmap.h>
|
||||
|
||||
#include <db.h>
|
||||
#include <limits.h>
|
||||
#include <kvm.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "kvm_private.h"
|
||||
|
||||
/* minidump must be the first item! */
|
||||
struct vmstate {
|
||||
int minidump; /* 1 = minidump mode */
|
||||
void *mmapbase;
|
||||
size_t mmapsize;
|
||||
};
|
||||
|
||||
void
|
||||
_kvm_freevtop(kvm_t *kd)
|
||||
{
|
||||
if (kd->vmst != 0) {
|
||||
if (kd->vmst->minidump)
|
||||
return (_kvm_minidump_freevtop(kd));
|
||||
if (kd->vmst->mmapbase != NULL)
|
||||
munmap(kd->vmst->mmapbase, kd->vmst->mmapsize);
|
||||
free(kd->vmst);
|
||||
kd->vmst = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_initvtop(kvm_t *kd)
|
||||
{
|
||||
char minihdr[8];
|
||||
|
||||
if (!kd->rawdump) {
|
||||
if (pread(kd->pmfd, &minihdr, 8, 0) == 8) {
|
||||
if (memcmp(&minihdr, "minidump", 8) == 0)
|
||||
return (_kvm_minidump_initvtop(kd));
|
||||
} else {
|
||||
_kvm_err(kd, kd->program, "cannot read header");
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
_kvm_err(kd, 0, "_kvm_initvtop: Unsupported image type");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
{
|
||||
|
||||
if (kd->vmst->minidump)
|
||||
return _kvm_minidump_kvatop(kd, va, pa);
|
||||
|
||||
|
||||
_kvm_err(kd, 0, "_kvm_kvatop: Unsupported image type");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Machine-dependent initialization for ALL open kvm descriptors,
|
||||
* not just those for a kernel crash dump. Some architectures
|
||||
* have to deal with these NOT being constants! (i.e. m68k)
|
||||
*/
|
||||
#ifdef FBSD_NOT_YET
|
||||
int
|
||||
_kvm_mdopen(kvm_t *kd __unused)
|
||||
{
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif
|
93
lib/libkvm/kvm_mips.h
Normal file
93
lib/libkvm/kvm_mips.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 John H. Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __KVM_MIPS_H__
|
||||
#define __KVM_MIPS_H__
|
||||
|
||||
#ifdef __mips__
|
||||
#include <machine/pte.h>
|
||||
#endif
|
||||
|
||||
typedef uint64_t mips_physaddr_t;
|
||||
|
||||
#define MIPS_PAGE_SHIFT 12
|
||||
#define MIPS_PAGE_SIZE (1 << MIPS_PAGE_SHIFT)
|
||||
#define MIPS_PAGE_MASK (MIPS_PAGE_SIZE - 1)
|
||||
|
||||
#define MIPS32_KSEG0_START 0x80000000
|
||||
#define MIPS32_KSEG0_END 0x9fffffff
|
||||
#define MIPS32_KSEG1_START 0xa0000000
|
||||
#define MIPS32_KSEG1_END 0xbfffffff
|
||||
#define MIPS64_KSEG0_START 0xffffffff80000000
|
||||
#define MIPS64_KSEG0_END 0xffffffff9fffffff
|
||||
#define MIPS64_KSEG1_START 0xffffffffa0000000
|
||||
#define MIPS64_KSEG1_END 0xffffffffbfffffff
|
||||
|
||||
#define MIPS32_PFN_MASK (0x1FFFFFC0)
|
||||
#define MIPS64_PFN_MASK 0x3FFFFFFC0
|
||||
#define MIPS_PFN_SHIFT (6)
|
||||
|
||||
#define MIPS_PFN_TO_PA(pfn) (((pfn) >> MIPS_PFN_SHIFT) << MIPS_PAGE_SHIFT)
|
||||
#define MIPS32_PTE_TO_PFN(pte) ((pte) & MIPS32_PFN_MASK)
|
||||
#define MIPS32_PTE_TO_PA(pte) (MIPS_PFN_TO_PA(MIPS32_PTE_TO_PFN((pte))))
|
||||
#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))))
|
||||
|
||||
#ifdef __mips__
|
||||
_Static_assert(PAGE_SHIFT == MIPS_PAGE_SHIFT, "PAGE_SHIFT mismatch");
|
||||
_Static_assert(PAGE_SIZE == MIPS_PAGE_SIZE, "PAGE_SIZE mismatch");
|
||||
_Static_assert(PAGE_MASK == MIPS_PAGE_MASK, "PAGE_MASK mismatch");
|
||||
#ifdef __mips_n64
|
||||
_Static_assert((uint64_t)MIPS_KSEG0_START == MIPS64_KSEG0_START,
|
||||
"MIPS_KSEG0_START mismatch");
|
||||
_Static_assert((uint64_t)MIPS_KSEG0_END == MIPS64_KSEG0_END,
|
||||
"MIPS_KSEG0_END mismatch");
|
||||
_Static_assert((uint64_t)MIPS_KSEG1_START == MIPS64_KSEG1_START,
|
||||
"MIPS_KSEG1_START mismatch");
|
||||
_Static_assert((uint64_t)MIPS_KSEG1_END == MIPS64_KSEG1_END,
|
||||
"MIPS_KSEG1_END mismatch");
|
||||
#else
|
||||
_Static_assert((uint32_t)MIPS_KSEG0_START == MIPS32_KSEG0_START,
|
||||
"MIPS_KSEG0_START mismatch");
|
||||
_Static_assert((uint32_t)MIPS_KSEG0_END == MIPS32_KSEG0_END,
|
||||
"MIPS_KSEG0_END mismatch");
|
||||
_Static_assert((uint32_t)MIPS_KSEG1_START == MIPS32_KSEG1_START,
|
||||
"MIPS_KSEG1_START mismatch");
|
||||
_Static_assert((uint32_t)MIPS_KSEG1_END == MIPS32_KSEG1_END,
|
||||
"MIPS_KSEG1_END mismatch");
|
||||
#endif
|
||||
#if defined(__mips_n64) || defined(__mips_n32)
|
||||
_Static_assert(TLBLO_PFN_MASK == MIPS64_PFN_MASK, "TLBLO_PFN_MASK mismatch");
|
||||
#else
|
||||
_Static_assert(TLBLO_PFN_MASK == MIPS32_PFN_MASK, "TLBLO_PFN_MASK mismatch");
|
||||
#endif
|
||||
_Static_assert(TLBLO_PFN_SHIFT == MIPS_PFN_SHIFT, "TLBLO_PFN_SHIFT mismatch");
|
||||
_Static_assert(TLB_PAGE_SHIFT == MIPS_PAGE_SHIFT, "TLB_PAGE_SHIFT mismatch");
|
||||
#endif
|
||||
|
||||
#endif /* !__KVM_MIPS_H__ */
|
62
lib/libkvm/kvm_native.3
Normal file
62
lib/libkvm/kvm_native.3
Normal file
@ -0,0 +1,62 @@
|
||||
.\"
|
||||
.\" Copyright (c) 2015 John Baldwin <jhb@FreeBSD.org>
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd November 27, 2015
|
||||
.Dt kvm_native 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm kvm_native
|
||||
.Nd is a kvm descriptor opened on a native kernel image
|
||||
.Sh LIBRARY
|
||||
.Lb libkvm
|
||||
.Sh SYNOPSIS
|
||||
.In kvm.h
|
||||
.Ft int
|
||||
.Fn kvm_native "kvm_t *kd"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm kvm
|
||||
library provides an interface for accessing kernel virtual memory images
|
||||
for both native kernel images
|
||||
.Pq where the ABI of the kernel executable matches the host system
|
||||
and non-native kernel images.
|
||||
The
|
||||
.Fn kvm_native
|
||||
function returns a non-zero value if the kvm descriptor
|
||||
.Fa kd
|
||||
is attached to a native kernel image;
|
||||
otherwise it returns zero.
|
||||
.Sh RETURN VALUES
|
||||
The
|
||||
.Fn kvm_native
|
||||
function returns a non-zero value if the kvm descriptor
|
||||
.Fa kd
|
||||
is attached to a native kernel image;
|
||||
otherwise it returns zero.
|
||||
.Sh SEE ALSO
|
||||
.Xr kvm 3 ,
|
||||
.Xr kvm_open2 3
|
@ -32,11 +32,12 @@
|
||||
.\" @(#)kvm_nlist.3 8.1 (Berkeley) 6/4/93
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd June 4, 1993
|
||||
.Dd November 27, 2015
|
||||
.Dt KVM_NLIST 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm kvm_nlist
|
||||
.Nm kvm_nlist ,
|
||||
.Nm kvm_nlist2
|
||||
.Nd retrieve symbol table names from a kernel image
|
||||
.Sh LIBRARY
|
||||
.Lb libkvm
|
||||
@ -45,31 +46,62 @@
|
||||
.In nlist.h
|
||||
.Ft int
|
||||
.Fn kvm_nlist "kvm_t *kd" "struct nlist *nl"
|
||||
.Ft int
|
||||
.Fn kvm_nlist2 "kvm_t *kd" "struct kvm_nlist *nl"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn kvm_nlist
|
||||
function retrieves the symbol table entries indicated by the name list argument
|
||||
.Fa \&nl .
|
||||
This argument points to an array of nlist structures, terminated by
|
||||
an entry whose n_name field is
|
||||
an entry whose
|
||||
.Fa n_name
|
||||
field is
|
||||
.Dv NULL
|
||||
(see
|
||||
.Xr nlist 3 ) .
|
||||
Each symbol is looked up using the n_name field, and if found, the
|
||||
corresponding n_type and n_value fields are filled in.
|
||||
Each symbol is looked up using the
|
||||
.Fa n_name
|
||||
field, and if found, the
|
||||
corresponding
|
||||
.Fa n_type
|
||||
and
|
||||
.Fa n_value
|
||||
fields are filled in.
|
||||
These fields are set
|
||||
to 0 if the symbol is not found.
|
||||
.Pp
|
||||
The
|
||||
.Xr kldsym 2
|
||||
system call is used to locate the symbol.
|
||||
system call is used to locate symbols in live kernels.
|
||||
This is a less than perfect
|
||||
emulation of the nlist values but has the advantage of being aware of kernel
|
||||
modules and is reasonably fast.
|
||||
.Pp
|
||||
The
|
||||
.Fn kvm_nlist2
|
||||
function retrieves the symbol table entries indicated by the name list argument
|
||||
.Fa nl .
|
||||
This argument points to an array of
|
||||
.Vt "struct kvm_nlist"
|
||||
structures,
|
||||
terminated by an entry whose
|
||||
.Fa n_name
|
||||
field is
|
||||
.Dv NULL
|
||||
These structures are similar to the nlist structures used by
|
||||
.Fn kvm_nlist
|
||||
except that the
|
||||
.Fa n_value
|
||||
field uses a different type
|
||||
.Pq Vt kvaddr_t
|
||||
to avoid truncation when examining non-native kernel images.
|
||||
.Sh RETURN VALUES
|
||||
The
|
||||
.Fn kvm_nlist
|
||||
function returns the number of invalid entries found.
|
||||
and
|
||||
.Fn kvm_nlist2
|
||||
functions return the number of invalid entries found.
|
||||
If the kernel symbol table was unreadable, -1 is returned.
|
||||
.Sh SEE ALSO
|
||||
.Xr kldsym 2 ,
|
||||
@ -79,6 +111,7 @@ If the kernel symbol table was unreadable, -1 is returned.
|
||||
.Xr kvm_getenvv 3 ,
|
||||
.Xr kvm_geterr 3 ,
|
||||
.Xr kvm_getprocs 3 ,
|
||||
.Xr kvm_native 3 ,
|
||||
.Xr kvm_open 3 ,
|
||||
.Xr kvm_openfiles 3 ,
|
||||
.Xr kvm_read 3 ,
|
||||
|
@ -32,11 +32,12 @@
|
||||
.\" @(#)kvm_open.3 8.3 (Berkeley) 4/19/94
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd January 29, 2004
|
||||
.Dd November 27, 2015
|
||||
.Dt KVM_OPEN 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm kvm_open ,
|
||||
.Nm kvm_open2 ,
|
||||
.Nm kvm_openfiles ,
|
||||
.Nm kvm_close
|
||||
.Nd initialize kernel virtual memory access
|
||||
@ -48,12 +49,21 @@
|
||||
.Ft kvm_t *
|
||||
.Fn kvm_open "const char *execfile" "const char *corefile" "const char *swapfile" "int flags" "const char *errstr"
|
||||
.Ft kvm_t *
|
||||
.Fo kvm_open2
|
||||
.Fa "const char *execfile"
|
||||
.Fa "const char *corefile"
|
||||
.Fa "int flags"
|
||||
.Fa "char *errbuf"
|
||||
.Fa "int (*resolver)(const char *name, kvaddr_t *addr)"
|
||||
.Fc
|
||||
.Ft kvm_t *
|
||||
.Fn kvm_openfiles "const char *execfile" "const char *corefile" "const char *swapfile" "int flags" "char *errbuf"
|
||||
.Ft int
|
||||
.Fn kvm_close "kvm_t *kd"
|
||||
.Sh DESCRIPTION
|
||||
The functions
|
||||
.Fn kvm_open
|
||||
.Fn kvm_open ,
|
||||
.Fn kvm_open2 ,
|
||||
and
|
||||
.Fn kvm_openfiles
|
||||
return a descriptor used to access kernel virtual memory
|
||||
@ -111,10 +121,13 @@ and
|
||||
.Dv O_RDWR
|
||||
are permitted.
|
||||
.Pp
|
||||
There are two open routines which differ only with respect to
|
||||
the error mechanism.
|
||||
The
|
||||
.Nm kvm
|
||||
library provides two different error reporting mechanisms.
|
||||
One provides backward compatibility with the SunOS kvm library, while the
|
||||
other provides an improved error reporting framework.
|
||||
The mechanism used by a descriptor is determined by the function used to
|
||||
open the descriptor.
|
||||
.Pp
|
||||
The
|
||||
.Fn kvm_open
|
||||
@ -140,8 +153,10 @@ The string is assumed to persist at least until the corresponding
|
||||
call.
|
||||
.Pp
|
||||
The
|
||||
.Fn kvm_open2
|
||||
and
|
||||
.Fn kvm_openfiles
|
||||
function provides
|
||||
functions provide
|
||||
.Bx
|
||||
style error reporting.
|
||||
Here, error messages are not printed out by the library.
|
||||
@ -160,25 +175,56 @@ on failure,
|
||||
.Fn kvm_geterr
|
||||
cannot be used to get the error message if open fails.
|
||||
Thus,
|
||||
.Fn kvm_open2
|
||||
and
|
||||
.Fn kvm_openfiles
|
||||
will place any error message in the
|
||||
.Fa errbuf
|
||||
argument.
|
||||
This buffer should be _POSIX2_LINE_MAX characters large (from
|
||||
<limits.h>).
|
||||
.Pp
|
||||
The
|
||||
.Fa resolver
|
||||
argument points to a function used by the
|
||||
.Nm kvm
|
||||
library to map symbol names to kernel virtual addresses.
|
||||
When the
|
||||
.Fa resolver
|
||||
function is called,
|
||||
.Fa name
|
||||
specifies the requested symbol name.
|
||||
If the function is able to resolve the name to an address,
|
||||
the address should be set in
|
||||
.Fa *addr
|
||||
and the function should return zero.
|
||||
If the function is not able to resolve the name to an address,
|
||||
it should return a non-zero value.
|
||||
When opening a native kernel image,
|
||||
.Fa resolver
|
||||
may be set to
|
||||
.Dv NULL
|
||||
to use an internal function to resolve symbol names.
|
||||
Non-native kernel images
|
||||
.Pq such as when cross-debugging a crash dump
|
||||
require a valid
|
||||
.Fa resolver .
|
||||
.Sh RETURN VALUES
|
||||
The
|
||||
.Fn kvm_open
|
||||
.Fn kvm_open ,
|
||||
.Fn kvm_open2 ,
|
||||
and
|
||||
.Fn kvm_openfiles
|
||||
functions both return a descriptor to be used
|
||||
functions return a descriptor to be used
|
||||
in all subsequent kvm library calls.
|
||||
The library is fully re-entrant.
|
||||
On failure,
|
||||
.Dv NULL
|
||||
is returned, in which case
|
||||
.Fn kvm_open2
|
||||
and
|
||||
.Fn kvm_openfiles
|
||||
writes the error message into
|
||||
write the error message into
|
||||
.Fa errbuf .
|
||||
.Pp
|
||||
The
|
||||
@ -191,13 +237,14 @@ function returns 0 on success and -1 on failure.
|
||||
.Xr kvm_getenvv 3 ,
|
||||
.Xr kvm_geterr 3 ,
|
||||
.Xr kvm_getprocs 3 ,
|
||||
.Xr kvm_native 3 ,
|
||||
.Xr kvm_nlist 3 ,
|
||||
.Xr kvm_read 3 ,
|
||||
.Xr kvm_write 3 ,
|
||||
.Xr kmem 4 ,
|
||||
.Xr mem 4
|
||||
.Sh BUGS
|
||||
There should not be two open calls.
|
||||
There should not be three open calls.
|
||||
The ill-defined error semantics
|
||||
of the Sun library and the desire to have a backward-compatible library
|
||||
for
|
||||
|
@ -216,7 +216,7 @@ _kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error)
|
||||
static int
|
||||
_kvm_dpcpu_init(kvm_t *kd)
|
||||
{
|
||||
struct nlist nl[] = {
|
||||
struct kvm_nlist nl[] = {
|
||||
#define NLIST_START_SET_PCPU 0
|
||||
{ .n_name = "___start_" DPCPU_SETNAME },
|
||||
#define NLIST_STOP_SET_PCPU 1
|
||||
@ -231,6 +231,12 @@ _kvm_dpcpu_init(kvm_t *kd)
|
||||
size_t len;
|
||||
u_int dpcpu_maxcpus;
|
||||
|
||||
/*
|
||||
* XXX: This only works for native kernels for now.
|
||||
*/
|
||||
if (!kvm_native(kd))
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* Locate and cache locations of important symbols using the internal
|
||||
* version of _kvm_nlist, turning off initialization to avoid
|
||||
@ -279,8 +285,8 @@ _kvm_dpcpu_initialized(kvm_t *kd, int intialize)
|
||||
* Check whether the value is within the dpcpu symbol range and only if so
|
||||
* adjust the offset relative to the current offset.
|
||||
*/
|
||||
uintptr_t
|
||||
_kvm_dpcpu_validaddr(kvm_t *kd, uintptr_t value)
|
||||
kvaddr_t
|
||||
_kvm_dpcpu_validaddr(kvm_t *kd, kvaddr_t value)
|
||||
{
|
||||
|
||||
if (value == 0)
|
||||
@ -319,6 +325,8 @@ ssize_t
|
||||
kvm_read_zpcpu(kvm_t *kd, u_long base, void *buf, size_t size, int cpu)
|
||||
{
|
||||
|
||||
if (!kvm_native(kd))
|
||||
return (-1);
|
||||
return (kvm_read(kd, (uintptr_t)(base + sizeof(struct pcpu) * cpu),
|
||||
buf, size));
|
||||
}
|
||||
|
@ -34,12 +34,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/kerneldump.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
|
||||
#include <db.h>
|
||||
#include <elf.h>
|
||||
#include <limits.h>
|
||||
#include <kvm.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -100,7 +97,7 @@ powerpc_maphdrs(kvm_t *kd)
|
||||
|
||||
vm = kd->vmst;
|
||||
|
||||
vm->mapsz = PAGE_SIZE;
|
||||
vm->mapsz = sizeof(*vm->eh) + sizeof(struct kerneldumpheader);
|
||||
vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
||||
if (vm->map == MAP_FAILED) {
|
||||
_kvm_err(kd, kd->program, "cannot map corefile");
|
||||
@ -130,7 +127,7 @@ powerpc_maphdrs(kvm_t *kd)
|
||||
vm->mapsz = vm->dmphdrsz + mapsz;
|
||||
vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
||||
if (vm->map == MAP_FAILED) {
|
||||
_kvm_err(kd, kd->program, "cannot map corefle headers");
|
||||
_kvm_err(kd, kd->program, "cannot map corefile headers");
|
||||
return (-1);
|
||||
}
|
||||
vm->eh = (void *)((uintptr_t)vm->map + vm->dmphdrsz);
|
||||
@ -138,8 +135,6 @@ powerpc_maphdrs(kvm_t *kd)
|
||||
return (0);
|
||||
|
||||
inval:
|
||||
munmap(vm->map, vm->mapsz);
|
||||
vm->map = MAP_FAILED;
|
||||
_kvm_err(kd, kd->program, "invalid corefile");
|
||||
return (-1);
|
||||
}
|
||||
@ -150,7 +145,7 @@ powerpc_maphdrs(kvm_t *kd)
|
||||
* 0 when the virtual address is invalid.
|
||||
*/
|
||||
static size_t
|
||||
powerpc_va2off(kvm_t *kd, u_long va, off_t *ofs)
|
||||
powerpc_va2off(kvm_t *kd, kvaddr_t va, off_t *ofs)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
Elf32_Phdr *ph;
|
||||
@ -172,48 +167,69 @@ powerpc_va2off(kvm_t *kd, u_long va, off_t *ofs)
|
||||
return (be32toh(ph->p_memsz) - (va - be32toh(ph->p_vaddr)));
|
||||
}
|
||||
|
||||
void
|
||||
_kvm_freevtop(kvm_t *kd)
|
||||
static void
|
||||
_powerpc_freevtop(kvm_t *kd)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
|
||||
if (vm == NULL)
|
||||
return;
|
||||
|
||||
if (vm->eh != MAP_FAILED) {
|
||||
if (vm->eh != MAP_FAILED)
|
||||
munmap(vm->eh, vm->mapsz);
|
||||
vm->eh = MAP_FAILED;
|
||||
}
|
||||
free(vm);
|
||||
kd->vmst = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_initvtop(kvm_t *kd)
|
||||
static int
|
||||
_powerpc_probe(kvm_t *kd)
|
||||
{
|
||||
|
||||
return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_PPC) &&
|
||||
kd->nlehdr.e_ident[EI_DATA] == ELFDATA2MSB);
|
||||
}
|
||||
|
||||
static int
|
||||
_powerpc_initvtop(kvm_t *kd)
|
||||
{
|
||||
|
||||
kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
|
||||
if (kd->vmst == NULL) {
|
||||
_kvm_err(kd, kd->program, "out of virtual memory");
|
||||
if (kd->vmst == NULL)
|
||||
return (-1);
|
||||
}
|
||||
if (powerpc_maphdrs(kd) == -1) {
|
||||
free(kd->vmst);
|
||||
kd->vmst = NULL;
|
||||
|
||||
if (powerpc_maphdrs(kd) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *ofs)
|
||||
static int
|
||||
_powerpc_kvatop(kvm_t *kd, kvaddr_t va, off_t *ofs)
|
||||
{
|
||||
struct vmstate *vm;
|
||||
|
||||
vm = kd->vmst;
|
||||
if (vm->ph->p_paddr == ~0U)
|
||||
if (be32toh(vm->ph->p_paddr) == 0xffffffff)
|
||||
return ((int)powerpc_va2off(kd, va, ofs));
|
||||
|
||||
_kvm_err(kd, kd->program, "Raw corefile not supported");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_powerpc_native(kvm_t *kd)
|
||||
{
|
||||
|
||||
#if defined(__powerpc__) && !defined(__powerpc64__)
|
||||
return (1);
|
||||
#else
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct kvm_arch kvm_powerpc = {
|
||||
.ka_probe = _powerpc_probe,
|
||||
.ka_initvtop = _powerpc_initvtop,
|
||||
.ka_freevtop = _powerpc_freevtop,
|
||||
.ka_kvatop = _powerpc_kvatop,
|
||||
.ka_native = _powerpc_native,
|
||||
};
|
||||
|
||||
KVM_ARCH(kvm_powerpc);
|
||||
|
@ -34,12 +34,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/kerneldump.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
|
||||
#include <db.h>
|
||||
#include <elf.h>
|
||||
#include <limits.h>
|
||||
#include <kvm.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -100,7 +97,7 @@ powerpc_maphdrs(kvm_t *kd)
|
||||
|
||||
vm = kd->vmst;
|
||||
|
||||
vm->mapsz = PAGE_SIZE;
|
||||
vm->mapsz = sizeof(*vm->eh) + sizeof(struct kerneldumpheader);
|
||||
vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
||||
if (vm->map == MAP_FAILED) {
|
||||
_kvm_err(kd, kd->program, "cannot map corefile");
|
||||
@ -130,16 +127,15 @@ powerpc_maphdrs(kvm_t *kd)
|
||||
vm->mapsz = vm->dmphdrsz + mapsz;
|
||||
vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
||||
if (vm->map == MAP_FAILED) {
|
||||
_kvm_err(kd, kd->program, "cannot map corefle headers");
|
||||
_kvm_err(kd, kd->program, "cannot map corefile headers");
|
||||
return (-1);
|
||||
}
|
||||
vm->eh = (void *)((uintptr_t)vm->map + vm->dmphdrsz);
|
||||
vm->ph = (void *)((uintptr_t)vm->eh + be64toh(vm->eh->e_phoff));
|
||||
vm->ph = (void *)((uintptr_t)vm->eh +
|
||||
(uintptr_t)be64toh(vm->eh->e_phoff));
|
||||
return (0);
|
||||
|
||||
inval:
|
||||
munmap(vm->map, vm->mapsz);
|
||||
vm->map = MAP_FAILED;
|
||||
_kvm_err(kd, kd->program, "invalid corefile");
|
||||
return (-1);
|
||||
}
|
||||
@ -150,7 +146,7 @@ powerpc_maphdrs(kvm_t *kd)
|
||||
* 0 when the virtual address is invalid.
|
||||
*/
|
||||
static size_t
|
||||
powerpc64_va2off(kvm_t *kd, u_long va, off_t *ofs)
|
||||
powerpc64_va2off(kvm_t *kd, kvaddr_t va, off_t *ofs)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
Elf64_Phdr *ph;
|
||||
@ -172,48 +168,69 @@ powerpc64_va2off(kvm_t *kd, u_long va, off_t *ofs)
|
||||
return (be64toh(ph->p_memsz) - (va - be64toh(ph->p_vaddr)));
|
||||
}
|
||||
|
||||
void
|
||||
_kvm_freevtop(kvm_t *kd)
|
||||
static void
|
||||
_powerpc64_freevtop(kvm_t *kd)
|
||||
{
|
||||
struct vmstate *vm = kd->vmst;
|
||||
|
||||
if (vm == NULL)
|
||||
return;
|
||||
|
||||
if (vm->eh != MAP_FAILED) {
|
||||
if (vm->eh != MAP_FAILED)
|
||||
munmap(vm->eh, vm->mapsz);
|
||||
vm->eh = MAP_FAILED;
|
||||
}
|
||||
free(vm);
|
||||
kd->vmst = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_initvtop(kvm_t *kd)
|
||||
static int
|
||||
_powerpc64_probe(kvm_t *kd)
|
||||
{
|
||||
|
||||
return (_kvm_probe_elf_kernel(kd, ELFCLASS64, EM_PPC64) &&
|
||||
kd->nlehdr.e_ident[EI_DATA] == ELFDATA2MSB);
|
||||
}
|
||||
|
||||
static int
|
||||
_powerpc64_initvtop(kvm_t *kd)
|
||||
{
|
||||
|
||||
kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
|
||||
if (kd->vmst == NULL) {
|
||||
_kvm_err(kd, kd->program, "out of virtual memory");
|
||||
if (kd->vmst == NULL)
|
||||
return (-1);
|
||||
}
|
||||
if (powerpc_maphdrs(kd) == -1) {
|
||||
free(kd->vmst);
|
||||
kd->vmst = NULL;
|
||||
|
||||
if (powerpc_maphdrs(kd) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *ofs)
|
||||
static int
|
||||
_powerpc64_kvatop(kvm_t *kd, kvaddr_t va, off_t *ofs)
|
||||
{
|
||||
struct vmstate *vm;
|
||||
|
||||
vm = kd->vmst;
|
||||
if (vm->ph->p_paddr == ~0UL)
|
||||
if (be64toh(vm->ph->p_paddr) == 0xffffffffffffffff)
|
||||
return ((int)powerpc64_va2off(kd, va, ofs));
|
||||
|
||||
_kvm_err(kd, kd->program, "Raw corefile not supported");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_powerpc64_native(kvm_t *kd)
|
||||
{
|
||||
|
||||
#ifdef __powerpc64__
|
||||
return (1);
|
||||
#else
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct kvm_arch kvm_powerpc64 = {
|
||||
.ka_probe = _powerpc64_probe,
|
||||
.ka_initvtop = _powerpc64_initvtop,
|
||||
.ka_freevtop = _powerpc64_freevtop,
|
||||
.ka_kvatop = _powerpc64_kvatop,
|
||||
.ka_native = _powerpc64_native,
|
||||
};
|
||||
|
||||
KVM_ARCH(kvm_powerpc64);
|
||||
|
@ -34,7 +34,22 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/endian.h>
|
||||
#include <sys/linker_set.h>
|
||||
#include <gelf.h>
|
||||
|
||||
struct kvm_arch {
|
||||
int (*ka_probe)(kvm_t *);
|
||||
int (*ka_initvtop)(kvm_t *);
|
||||
void (*ka_freevtop)(kvm_t *);
|
||||
int (*ka_kvatop)(kvm_t *, kvaddr_t, off_t *);
|
||||
int (*ka_native)(kvm_t *);
|
||||
};
|
||||
|
||||
#define KVM_ARCH(ka) DATA_SET(kvm_arch, ka)
|
||||
|
||||
struct __kvm {
|
||||
struct kvm_arch *arch;
|
||||
/*
|
||||
* a string to be prepended to error messages
|
||||
* provided for compatibility with sun's interface
|
||||
@ -46,8 +61,9 @@ struct __kvm {
|
||||
#define ISALIVE(kd) ((kd)->vmfd >= 0)
|
||||
int pmfd; /* physical memory file (or crashdump) */
|
||||
int vmfd; /* virtual memory file (-1 if crashdump) */
|
||||
int unused; /* was: swap file (e.g., /dev/drum) */
|
||||
int nlfd; /* namelist file (e.g., /kernel) */
|
||||
GElf_Ehdr nlehdr; /* ELF file header for namelist file */
|
||||
int (*resolve_symbol)(const char *, kvaddr_t *);
|
||||
struct kinfo_proc *procbase;
|
||||
char *argspc; /* (dynamic) storage for argv strings */
|
||||
int arglen; /* length of the above */
|
||||
@ -64,10 +80,10 @@ struct __kvm {
|
||||
int rawdump; /* raw dump format */
|
||||
|
||||
int vnet_initialized; /* vnet fields set up */
|
||||
uintptr_t vnet_start; /* start of kernel's vnet region */
|
||||
uintptr_t vnet_stop; /* stop of kernel's vnet region */
|
||||
uintptr_t vnet_current; /* vnet we're working with */
|
||||
uintptr_t vnet_base; /* vnet base of current vnet */
|
||||
kvaddr_t vnet_start; /* start of kernel's vnet region */
|
||||
kvaddr_t vnet_stop; /* stop of kernel's vnet region */
|
||||
kvaddr_t vnet_current; /* vnet we're working with */
|
||||
kvaddr_t vnet_base; /* vnet base of current vnet */
|
||||
|
||||
/*
|
||||
* Dynamic per-CPU kernel memory. We translate symbols, on-demand,
|
||||
@ -75,38 +91,69 @@ struct __kvm {
|
||||
* kvm_dpcpu_setcpu().
|
||||
*/
|
||||
int dpcpu_initialized; /* dpcpu fields set up */
|
||||
uintptr_t dpcpu_start; /* start of kernel's dpcpu region */
|
||||
uintptr_t dpcpu_stop; /* stop of kernel's dpcpu region */
|
||||
kvaddr_t dpcpu_start; /* start of kernel's dpcpu region */
|
||||
kvaddr_t dpcpu_stop; /* stop of kernel's dpcpu region */
|
||||
u_int dpcpu_maxcpus; /* size of base array */
|
||||
uintptr_t *dpcpu_off; /* base array, indexed by CPU ID */
|
||||
u_int dpcpu_curcpu; /* CPU we're currently working with */
|
||||
uintptr_t dpcpu_curoff; /* dpcpu base of current CPU */
|
||||
kvaddr_t dpcpu_curoff; /* dpcpu base of current CPU */
|
||||
};
|
||||
|
||||
/*
|
||||
* Page table hash used by minidump backends to map physical addresses
|
||||
* to file offsets.
|
||||
*/
|
||||
struct hpte {
|
||||
struct hpte *next;
|
||||
uint64_t pa;
|
||||
off_t off;
|
||||
};
|
||||
|
||||
#define HPT_SIZE 1024
|
||||
|
||||
struct hpt {
|
||||
struct hpte *hpt_head[HPT_SIZE];
|
||||
};
|
||||
|
||||
/*
|
||||
* Functions used internally by kvm, but across kvm modules.
|
||||
*/
|
||||
static inline uint32_t
|
||||
_kvm32toh(kvm_t *kd, uint32_t val)
|
||||
{
|
||||
|
||||
if (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
return (le32toh(val));
|
||||
else
|
||||
return (be32toh(val));
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
_kvm64toh(kvm_t *kd, uint64_t val)
|
||||
{
|
||||
|
||||
if (kd->nlehdr.e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
return (le64toh(val));
|
||||
else
|
||||
return (be64toh(val));
|
||||
}
|
||||
|
||||
void _kvm_err(kvm_t *kd, const char *program, const char *fmt, ...)
|
||||
__printflike(3, 4);
|
||||
void _kvm_freeprocs(kvm_t *kd);
|
||||
void _kvm_freevtop(kvm_t *);
|
||||
int _kvm_initvtop(kvm_t *);
|
||||
int _kvm_kvatop(kvm_t *, u_long, off_t *);
|
||||
void *_kvm_malloc(kvm_t *kd, size_t);
|
||||
int _kvm_nlist(kvm_t *, struct nlist *, int);
|
||||
int _kvm_nlist(kvm_t *, struct kvm_nlist *, int);
|
||||
void *_kvm_realloc(kvm_t *kd, void *, size_t);
|
||||
void _kvm_syserr (kvm_t *kd, const char *program, const char *fmt, ...)
|
||||
__printflike(3, 4);
|
||||
int _kvm_uvatop(kvm_t *, const struct proc *, u_long, u_long *);
|
||||
int _kvm_vnet_selectpid(kvm_t *, pid_t);
|
||||
int _kvm_vnet_initialized(kvm_t *, int);
|
||||
uintptr_t _kvm_vnet_validaddr(kvm_t *, uintptr_t);
|
||||
kvaddr_t _kvm_vnet_validaddr(kvm_t *, kvaddr_t);
|
||||
int _kvm_dpcpu_initialized(kvm_t *, int);
|
||||
uintptr_t _kvm_dpcpu_validaddr(kvm_t *, uintptr_t);
|
||||
|
||||
#if defined(__aarch64__) || defined(__amd64__) || defined(__arm__) || \
|
||||
defined(__i386__) || defined(__mips__)
|
||||
void _kvm_minidump_freevtop(kvm_t *);
|
||||
int _kvm_minidump_initvtop(kvm_t *);
|
||||
int _kvm_minidump_kvatop(kvm_t *, u_long, off_t *);
|
||||
#endif
|
||||
kvaddr_t _kvm_dpcpu_validaddr(kvm_t *, kvaddr_t);
|
||||
int _kvm_probe_elf_kernel(kvm_t *, int, int);
|
||||
int _kvm_is_minidump(kvm_t *);
|
||||
int _kvm_read_core_phdrs(kvm_t *, size_t *, GElf_Phdr **);
|
||||
void _kvm_hpt_init(kvm_t *, struct hpt *, void *, size_t, off_t, int, int);
|
||||
off_t _kvm_hpt_find(struct hpt *, uint64_t);
|
||||
void _kvm_hpt_free(struct hpt *);
|
||||
|
@ -582,6 +582,12 @@ kvm_getprocs(kvm_t *kd, int op, int arg, int *cnt)
|
||||
nl[5].n_name = "_cpu_tick_frequency";
|
||||
nl[6].n_name = 0;
|
||||
|
||||
if (!kd->arch->ka_native(kd)) {
|
||||
_kvm_err(kd, kd->program,
|
||||
"cannot read procs from non-native core");
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (kvm_nlist(kd, nl) != 0) {
|
||||
for (p = nl; p->n_type != 0; ++p)
|
||||
;
|
||||
|
@ -32,11 +32,12 @@
|
||||
.\" @(#)kvm_read.3 8.1 (Berkeley) 6/4/93
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd June 4, 1993
|
||||
.Dd November 27, 2015
|
||||
.Dt KVM_READ 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm kvm_read ,
|
||||
.Nm kvm_read2 ,
|
||||
.Nm kvm_write
|
||||
.Nd read or write kernel virtual memory
|
||||
.Sh LIBRARY
|
||||
@ -46,23 +47,26 @@
|
||||
.Ft ssize_t
|
||||
.Fn kvm_read "kvm_t *kd" "unsigned long addr" "void *buf" "size_t nbytes"
|
||||
.Ft ssize_t
|
||||
.Fn kvm_read2 "kvm_t *kd" "kvaddr_t addr" "void *buf" "size_t nbytes"
|
||||
.Ft ssize_t
|
||||
.Fn kvm_write "kvm_t *kd" "unsigned long addr" "const void *buf" "size_t nbytes"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn kvm_read
|
||||
.Fn kvm_read ,
|
||||
.Fn kvm_read2 ,
|
||||
and
|
||||
.Fn kvm_write
|
||||
functions are used to read and write kernel virtual memory (or a crash
|
||||
dump file).
|
||||
See
|
||||
.Fn kvm_open 3
|
||||
or
|
||||
.Fn kvm_openfiles 3
|
||||
for information regarding opening kernel virtual memory and crash dumps.
|
||||
.Pp
|
||||
The
|
||||
.Fn kvm_read
|
||||
function transfers
|
||||
and
|
||||
.Fn kvm_read2
|
||||
functions transfer
|
||||
.Fa nbytes
|
||||
bytes of data from
|
||||
the kernel space address
|
||||
@ -77,6 +81,16 @@ to
|
||||
.Fa addr .
|
||||
Unlike their SunOS counterparts, these functions cannot be used to
|
||||
read or write process address spaces.
|
||||
.Pp
|
||||
The
|
||||
.Fn kvm_read2
|
||||
function uses a different type
|
||||
.Pq Vt kvaddr_t
|
||||
for the
|
||||
.Fa addr
|
||||
argument to allow use of addresses larger than
|
||||
.Dv ULONG_MAX
|
||||
when examining non-native kernel images.
|
||||
.Sh RETURN VALUES
|
||||
Upon success, the number of bytes actually transferred is returned.
|
||||
Otherwise, -1 is returned.
|
||||
|
@ -47,65 +47,54 @@ static char sccsid[] = "@(#)kvm_hp300.c 8.1 (Berkeley) 6/4/93";
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/stat.h>
|
||||
#include <kvm.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <nlist.h>
|
||||
#include <kvm.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
|
||||
#include <machine/kerneldump.h>
|
||||
#include <machine/tte.h>
|
||||
#include <machine/tlb.h>
|
||||
#include <machine/tsb.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include "../../sys/sparc64/include/kerneldump.h"
|
||||
|
||||
#include "kvm_private.h"
|
||||
|
||||
#ifndef btop
|
||||
#define btop(x) (sparc64_btop(x))
|
||||
#define ptob(x) (sparc64_ptob(x))
|
||||
#endif
|
||||
#include "kvm_sparc64.h"
|
||||
|
||||
struct vmstate {
|
||||
off_t vm_tsb_off;
|
||||
vm_size_t vm_tsb_mask;
|
||||
uint64_t vm_tsb_mask;
|
||||
int vm_nregions;
|
||||
struct sparc64_dump_reg *vm_regions;
|
||||
};
|
||||
|
||||
void
|
||||
_kvm_freevtop(kvm_t *kd)
|
||||
static int
|
||||
_sparc64_probe(kvm_t *kd)
|
||||
{
|
||||
if (kd->vmst != 0) {
|
||||
free(kd->vmst->vm_regions);
|
||||
free(kd->vmst);
|
||||
}
|
||||
|
||||
return (_kvm_probe_elf_kernel(kd, ELFCLASS64, EM_SPARCV9));
|
||||
}
|
||||
|
||||
static void
|
||||
_sparc64_freevtop(kvm_t *kd)
|
||||
{
|
||||
|
||||
free(kd->vmst->vm_regions);
|
||||
free(kd->vmst);
|
||||
kd->vmst = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
_kvm_read_phys(kvm_t *kd, off_t pos, void *buf, size_t size)
|
||||
_sparc64_read_phys(kvm_t *kd, off_t pos, void *buf, size_t size)
|
||||
{
|
||||
|
||||
/* XXX This has to be a raw file read, kvm_read is virtual. */
|
||||
if (lseek(kd->pmfd, pos, SEEK_SET) == -1) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_read_phys: lseek");
|
||||
return (0);
|
||||
}
|
||||
if (read(kd->pmfd, buf, size) != (ssize_t)size) {
|
||||
_kvm_syserr(kd, kd->program, "_kvm_read_phys: read");
|
||||
if (pread(kd->pmfd, buf, size, pos) != (ssize_t)size) {
|
||||
_kvm_syserr(kd, kd->program, "_sparc64_read_phys: pread");
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
_kvm_reg_cmp(const void *a, const void *b)
|
||||
_sparc64_reg_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct sparc64_dump_reg *ra, *rb;
|
||||
|
||||
@ -122,14 +111,14 @@ _kvm_reg_cmp(const void *a, const void *b)
|
||||
#define KVM_OFF_NOTFOUND 0
|
||||
|
||||
static off_t
|
||||
_kvm_find_off(struct vmstate *vm, vm_offset_t pa, vm_size_t size)
|
||||
_sparc64_find_off(struct vmstate *vm, uint64_t pa, uint64_t size)
|
||||
{
|
||||
struct sparc64_dump_reg *reg, key;
|
||||
vm_offset_t o;
|
||||
|
||||
key.dr_pa = pa;
|
||||
reg = bsearch(&key, vm->vm_regions, vm->vm_nregions,
|
||||
sizeof(*vm->vm_regions), _kvm_reg_cmp);
|
||||
sizeof(*vm->vm_regions), _sparc64_reg_cmp);
|
||||
if (reg == NULL)
|
||||
return (KVM_OFF_NOTFOUND);
|
||||
o = pa - reg->dr_pa;
|
||||
@ -138,14 +127,15 @@ _kvm_find_off(struct vmstate *vm, vm_offset_t pa, vm_size_t size)
|
||||
return (reg->dr_offs + o);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_initvtop(kvm_t *kd)
|
||||
static int
|
||||
_sparc64_initvtop(kvm_t *kd)
|
||||
{
|
||||
struct sparc64_dump_hdr hdr;
|
||||
struct sparc64_dump_reg *regs;
|
||||
struct vmstate *vm;
|
||||
size_t regsz;
|
||||
vm_offset_t pa;
|
||||
uint64_t pa;
|
||||
int i;
|
||||
|
||||
vm = (struct vmstate *)_kvm_malloc(kd, sizeof(*vm));
|
||||
if (vm == NULL) {
|
||||
@ -154,8 +144,13 @@ _kvm_initvtop(kvm_t *kd)
|
||||
}
|
||||
kd->vmst = vm;
|
||||
|
||||
if (!_kvm_read_phys(kd, 0, &hdr, sizeof(hdr)))
|
||||
if (!_sparc64_read_phys(kd, 0, &hdr, sizeof(hdr)))
|
||||
goto fail_vm;
|
||||
hdr.dh_hdr_size = be64toh(hdr.dh_hdr_size);
|
||||
hdr.dh_tsb_pa = be64toh(hdr.dh_tsb_pa);
|
||||
hdr.dh_tsb_size = be64toh(hdr.dh_tsb_size);
|
||||
hdr.dh_tsb_mask = be64toh(hdr.dh_tsb_mask);
|
||||
hdr.dh_nregions = be32toh(hdr.dh_nregions);
|
||||
pa = hdr.dh_tsb_pa;
|
||||
|
||||
regsz = hdr.dh_nregions * sizeof(*regs);
|
||||
@ -164,14 +159,19 @@ _kvm_initvtop(kvm_t *kd)
|
||||
_kvm_err(kd, kd->program, "cannot allocate regions");
|
||||
goto fail_vm;
|
||||
}
|
||||
if (!_kvm_read_phys(kd, sizeof(hdr), regs, regsz))
|
||||
if (!_sparc64_read_phys(kd, sizeof(hdr), regs, regsz))
|
||||
goto fail_regs;
|
||||
qsort(regs, hdr.dh_nregions, sizeof(*regs), _kvm_reg_cmp);
|
||||
for (i = 0; i < hdr.dh_nregions; i++) {
|
||||
regs[i].dr_pa = be64toh(regs[i].dr_pa);
|
||||
regs[i].dr_size = be64toh(regs[i].dr_size);
|
||||
regs[i].dr_offs = be64toh(regs[i].dr_offs);
|
||||
}
|
||||
qsort(regs, hdr.dh_nregions, sizeof(*regs), _sparc64_reg_cmp);
|
||||
|
||||
vm->vm_tsb_mask = hdr.dh_tsb_mask;
|
||||
vm->vm_regions = regs;
|
||||
vm->vm_nregions = hdr.dh_nregions;
|
||||
vm->vm_tsb_off = _kvm_find_off(vm, hdr.dh_tsb_pa, hdr.dh_tsb_size);
|
||||
vm->vm_tsb_off = _sparc64_find_off(vm, hdr.dh_tsb_pa, hdr.dh_tsb_size);
|
||||
if (vm->vm_tsb_off == KVM_OFF_NOTFOUND) {
|
||||
_kvm_err(kd, kd->program, "tsb not found in dump");
|
||||
goto fail_regs;
|
||||
@ -185,37 +185,60 @@ _kvm_initvtop(kvm_t *kd)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
||||
static int
|
||||
_sparc64_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||
{
|
||||
struct tte tte;
|
||||
struct sparc64_tte tte;
|
||||
off_t tte_off;
|
||||
u_long vpn;
|
||||
kvaddr_t vpn;
|
||||
off_t pa_off;
|
||||
u_long pg_off;
|
||||
kvaddr_t pg_off;
|
||||
int rest;
|
||||
|
||||
pg_off = va & PAGE_MASK;
|
||||
if (va >= VM_MIN_DIRECT_ADDRESS)
|
||||
pa_off = TLB_DIRECT_TO_PHYS(va) & ~PAGE_MASK;
|
||||
pg_off = va & SPARC64_PAGE_MASK;
|
||||
if (va >= SPARC64_MIN_DIRECT_ADDRESS)
|
||||
pa_off = SPARC64_DIRECT_TO_PHYS(va) & ~SPARC64_PAGE_MASK;
|
||||
else {
|
||||
vpn = btop(va);
|
||||
vpn = va >> SPARC64_PAGE_SHIFT;
|
||||
tte_off = kd->vmst->vm_tsb_off +
|
||||
((vpn & kd->vmst->vm_tsb_mask) << TTE_SHIFT);
|
||||
if (!_kvm_read_phys(kd, tte_off, &tte, sizeof(tte)))
|
||||
((vpn & kd->vmst->vm_tsb_mask) << SPARC64_TTE_SHIFT);
|
||||
if (!_sparc64_read_phys(kd, tte_off, &tte, sizeof(tte)))
|
||||
goto invalid;
|
||||
if (!tte_match(&tte, va))
|
||||
tte.tte_vpn = be64toh(tte.tte_vpn);
|
||||
tte.tte_data = be64toh(tte.tte_data);
|
||||
if (!sparc64_tte_match(&tte, va))
|
||||
goto invalid;
|
||||
pa_off = TTE_GET_PA(&tte);
|
||||
pa_off = SPARC64_TTE_GET_PA(&tte);
|
||||
}
|
||||
rest = PAGE_SIZE - pg_off;
|
||||
pa_off = _kvm_find_off(kd->vmst, pa_off, rest);
|
||||
rest = SPARC64_PAGE_SIZE - pg_off;
|
||||
pa_off = _sparc64_find_off(kd->vmst, pa_off, rest);
|
||||
if (pa_off == KVM_OFF_NOTFOUND)
|
||||
goto invalid;
|
||||
*pa = pa_off + pg_off;
|
||||
return (rest);
|
||||
|
||||
invalid:
|
||||
_kvm_err(kd, 0, "invalid address (%lx)", va);
|
||||
_kvm_err(kd, 0, "invalid address (%jx)", (uintmax_t)va);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_sparc64_native(kvm_t *kd)
|
||||
{
|
||||
|
||||
#ifdef __sparc64__
|
||||
return (1);
|
||||
#else
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct kvm_arch kvm_sparc64 = {
|
||||
.ka_probe = _sparc64_probe,
|
||||
.ka_initvtop = _sparc64_initvtop,
|
||||
.ka_freevtop = _sparc64_freevtop,
|
||||
.ka_kvatop = _sparc64_kvatop,
|
||||
.ka_native = _sparc64_native,
|
||||
};
|
||||
|
||||
KVM_ARCH(kvm_sparc64);
|
||||
|
118
lib/libkvm/kvm_sparc64.h
Normal file
118
lib/libkvm/kvm_sparc64.h
Normal file
@ -0,0 +1,118 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 John H. Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __KVM_SPARC64_H__
|
||||
#define __KVM_SPARC64_H__
|
||||
|
||||
#ifdef __sparc64__
|
||||
#include <sys/queue.h>
|
||||
#include <machine/tlb.h>
|
||||
#include <machine/tte.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#endif
|
||||
|
||||
#define SPARC64_PAGE_SHIFT 13
|
||||
#define SPARC64_PAGE_SIZE (1 << SPARC64_PAGE_SHIFT)
|
||||
#define SPARC64_PAGE_MASK (SPARC64_PAGE_SIZE - 1)
|
||||
|
||||
#define SPARC64_MIN_DIRECT_ADDRESS (0xfffff80000000000)
|
||||
|
||||
#define SPARC64_DIRECT_ADDRESS_BITS (43)
|
||||
#define SPARC64_DIRECT_ADDRESS_MASK \
|
||||
(((uint64_t)1 << SPARC64_DIRECT_ADDRESS_BITS) - 1)
|
||||
|
||||
#define SPARC64_DIRECT_TO_PHYS(va) ((va) & SPARC64_DIRECT_ADDRESS_MASK)
|
||||
|
||||
#define SPARC64_TTE_SHIFT (5)
|
||||
|
||||
#define SPARC64_TD_SIZE_SHIFT (61)
|
||||
#define SPARC64_TD_PA_SHIFT (13)
|
||||
|
||||
#define SPARC64_TD_SIZE_BITS (2)
|
||||
#define SPARC64_TD_PA_CH_BITS (30) /* US-III{,i,+}, US-IV{,+}, SPARC64 V */
|
||||
#define SPARC64_TD_PA_BITS SPARC64_TD_PA_CH_BITS
|
||||
|
||||
#define SPARC64_TD_SIZE_MASK (((uint64_t)1 << SPARC64_TD_SIZE_BITS) - 1)
|
||||
#define SPARC64_TD_PA_MASK (((uint64_t)1 << SPARC64_TD_PA_BITS) - 1)
|
||||
|
||||
#define SPARC64_TD_V ((uint64_t)1 << 63)
|
||||
|
||||
#define SPARC64_TV_SIZE_BITS (SPARC64_TD_SIZE_BITS)
|
||||
#define SPARC64_TV_VPN(va, sz) \
|
||||
((((va) >> SPARC64_TTE_PAGE_SHIFT(sz)) << SPARC64_TV_SIZE_BITS) | sz)
|
||||
|
||||
#define SPARC64_TTE_SIZE_SPREAD (3)
|
||||
#define SPARC64_TTE_PAGE_SHIFT(sz) \
|
||||
(SPARC64_PAGE_SHIFT + ((sz) * SPARC64_TTE_SIZE_SPREAD))
|
||||
|
||||
#define SPARC64_TTE_GET_SIZE(tp) \
|
||||
(((tp)->tte_data >> SPARC64_TD_SIZE_SHIFT) & SPARC64_TD_SIZE_MASK)
|
||||
|
||||
#define SPARC64_TTE_GET_PA(tp) \
|
||||
((tp)->tte_data & (SPARC64_TD_PA_MASK << SPARC64_TD_PA_SHIFT))
|
||||
|
||||
struct sparc64_tte {
|
||||
uint64_t tte_vpn;
|
||||
uint64_t tte_data;
|
||||
};
|
||||
|
||||
static __inline int
|
||||
sparc64_tte_match(struct sparc64_tte *tp, kvaddr_t va)
|
||||
{
|
||||
|
||||
return (((tp->tte_data & SPARC64_TD_V) != 0) &&
|
||||
(tp->tte_vpn == SPARC64_TV_VPN(va, SPARC64_TTE_GET_SIZE(tp))));
|
||||
}
|
||||
|
||||
#ifdef __sparc64__
|
||||
_Static_assert(PAGE_SHIFT == SPARC64_PAGE_SHIFT, "PAGE_SHIFT mismatch");
|
||||
_Static_assert(PAGE_SIZE == SPARC64_PAGE_SIZE, "PAGE_SIZE mismatch");
|
||||
_Static_assert(PAGE_MASK == SPARC64_PAGE_MASK, "PAGE_MASK mismatch");
|
||||
_Static_assert(VM_MIN_DIRECT_ADDRESS == SPARC64_MIN_DIRECT_ADDRESS,
|
||||
"VM_MIN_DIRECT_ADDRESS mismatch");
|
||||
_Static_assert(TLB_DIRECT_ADDRESS_BITS == SPARC64_DIRECT_ADDRESS_BITS,
|
||||
"TLB_DIRECT_ADDRESS_BITS mismatch");
|
||||
_Static_assert(TLB_DIRECT_ADDRESS_MASK == SPARC64_DIRECT_ADDRESS_MASK,
|
||||
"TLB_DIRECT_ADDRESS_MASK mismatch");
|
||||
_Static_assert(TTE_SHIFT == SPARC64_TTE_SHIFT, "TTE_SHIFT mismatch");
|
||||
_Static_assert(TD_SIZE_SHIFT == SPARC64_TD_SIZE_SHIFT,
|
||||
"TD_SIZE_SHIFT mismatch");
|
||||
_Static_assert(TD_PA_SHIFT == SPARC64_TD_PA_SHIFT,
|
||||
"TD_PA_SHIFT mismatch");
|
||||
_Static_assert(TD_SIZE_BITS == SPARC64_TD_SIZE_BITS, "TD_SIZE_BITS mismatch");
|
||||
_Static_assert(TD_PA_BITS == SPARC64_TD_PA_BITS, "TD_PA_BITS mismatch");
|
||||
_Static_assert(TD_SIZE_MASK == SPARC64_TD_SIZE_MASK, "TD_SIZE_MASK mismatch");
|
||||
_Static_assert(TD_PA_MASK == SPARC64_TD_PA_MASK, "TD_PA_MASK mismatch");
|
||||
_Static_assert(TD_V == SPARC64_TD_V, "TD_V mismatch");
|
||||
_Static_assert(TV_SIZE_BITS == SPARC64_TV_SIZE_BITS, "TV_SIZE_BITS mismatch");
|
||||
_Static_assert(TTE_SIZE_SPREAD == SPARC64_TTE_SIZE_SPREAD,
|
||||
"TTE_SIZE_SPREAD mismatch");
|
||||
#endif
|
||||
|
||||
#endif /* !__KVM_SPARC64_H__ */
|
@ -43,7 +43,6 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <net/vnet.h>
|
||||
|
||||
#include <nlist.h>
|
||||
#include <kvm.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
@ -62,7 +61,7 @@ _kvm_vnet_selectpid(kvm_t *kd, pid_t pid)
|
||||
struct ucred cred;
|
||||
struct prison prison;
|
||||
struct vnet vnet;
|
||||
struct nlist nl[] = {
|
||||
struct kvm_nlist nl[] = {
|
||||
/*
|
||||
* Note: kvm_nlist strips the first '_' so add an extra one
|
||||
* here to __{start,stop}_set_vnet.
|
||||
@ -89,6 +88,12 @@ _kvm_vnet_selectpid(kvm_t *kd, pid_t pid)
|
||||
#endif
|
||||
lwpid_t dumptid;
|
||||
|
||||
/*
|
||||
* XXX: This only works for native kernels for now.
|
||||
*/
|
||||
if (!kvm_native(kd))
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* Locate and cache locations of important symbols
|
||||
* using the internal version of _kvm_nlist, turning
|
||||
@ -204,7 +209,7 @@ _kvm_vnet_selectpid(kvm_t *kd, pid_t pid)
|
||||
|
||||
/*
|
||||
* Check whether the vnet module has been initialized sucessfully
|
||||
* or not, intialize it if permitted.
|
||||
* or not, initialize it if permitted.
|
||||
*/
|
||||
int
|
||||
_kvm_vnet_initialized(kvm_t *kd, int intialize)
|
||||
@ -222,8 +227,8 @@ _kvm_vnet_initialized(kvm_t *kd, int intialize)
|
||||
* Check whether the value is within the vnet symbol range and
|
||||
* only if so adjust the offset relative to the current base.
|
||||
*/
|
||||
uintptr_t
|
||||
_kvm_vnet_validaddr(kvm_t *kd, uintptr_t value)
|
||||
kvaddr_t
|
||||
_kvm_vnet_validaddr(kvm_t *kd, kvaddr_t value)
|
||||
{
|
||||
|
||||
if (value == 0)
|
||||
|
@ -53,7 +53,7 @@ CRUNCH_PROGS_bin= cat chflags chio chmod cp date dd df echo \
|
||||
ed expr getfacl hostname kenv kill ln ls mkdir mv \
|
||||
pkill ps pwd realpath rm rmdir setfacl sh sleep stty \
|
||||
sync test
|
||||
CRUNCH_LIBS+= -lcrypt -ledit -ljail -lkvm -ll -ltermcapw -lutil -lxo
|
||||
CRUNCH_LIBS+= -lcrypt -ledit -ljail -lkvm -lelf -ll -ltermcapw -lutil -lxo
|
||||
CRUNCH_BUILDTOOLS+= bin/sh
|
||||
|
||||
# Additional options for specific programs
|
||||
|
@ -180,6 +180,7 @@ _DP_geom= bsdxml sbuf
|
||||
_DP_cam= sbuf
|
||||
_DP_casper= capsicum nv pjdlog
|
||||
_DP_capsicum= nv
|
||||
_DP_kvm= elf
|
||||
_DP_pjdlog= util
|
||||
_DP_opie= md
|
||||
_DP_usb= pthread
|
||||
|
@ -29,9 +29,9 @@
|
||||
#define _MACHINE_KERNELDUMP_H_
|
||||
|
||||
struct sparc64_dump_reg {
|
||||
vm_paddr_t dr_pa;
|
||||
vm_offset_t dr_size;
|
||||
vm_offset_t dr_offs;
|
||||
uint64_t dr_pa;
|
||||
uint64_t dr_size;
|
||||
uint64_t dr_offs;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -40,11 +40,12 @@ struct sparc64_dump_reg {
|
||||
* would require some ugly hacks.
|
||||
*/
|
||||
struct sparc64_dump_hdr {
|
||||
vm_offset_t dh_hdr_size;
|
||||
vm_paddr_t dh_tsb_pa;
|
||||
vm_size_t dh_tsb_size;
|
||||
vm_size_t dh_tsb_mask;
|
||||
int dh_nregions;
|
||||
uint64_t dh_hdr_size;
|
||||
uint64_t dh_tsb_pa;
|
||||
uint64_t dh_tsb_size;
|
||||
uint64_t dh_tsb_mask;
|
||||
int32_t dh_nregions;
|
||||
int32_t dh_pad;
|
||||
struct sparc64_dump_reg dh_regions[];
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user