mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
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
42 changed files with 2386 additions and 1603 deletions
|
|
@ -1819,6 +1819,7 @@ _prebuild_libs+= lib/libc++
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
lib/libgeom__L: lib/libexpat__L
|
lib/libgeom__L: lib/libexpat__L
|
||||||
|
lib/libkvm__L: lib/libelf__L
|
||||||
|
|
||||||
.if ${MK_LIBTHR} != "no"
|
.if ${MK_LIBTHR} != "no"
|
||||||
_lib_libthr= lib/libthr
|
_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
|
GDBLIBS= ${OBJ_GDB}/libgdb/libgdb.a
|
||||||
|
|
||||||
DPADD= ${GDBLIBS} ${BULIBS} ${LIBKVM}
|
DPADD= ${GDBLIBS} ${BULIBS} ${LIBKVM}
|
||||||
LDADD= ${GDBLIBS} ${BULIBS} -lkvm${GDB_SUFFIX}
|
LDADD= ${GDBLIBS} ${BULIBS} -lkvm
|
||||||
LIBADD+= m readline ncursesw gnuregex
|
LIBADD+= m readline ncursesw gnuregex
|
||||||
|
|
||||||
.if defined(GDB_CROSS_DEBUGGER)
|
|
||||||
CFLAGS+= -Wl,-export-dynamic
|
|
||||||
.endif
|
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
.include <bsd.prog.mk>
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,6 @@ __FBSDID("$FreeBSD$");
|
||||||
#include <kvm.h>
|
#include <kvm.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
#ifdef CROSS_DEBUGGER
|
|
||||||
#include <proc_service.h>
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -81,24 +78,6 @@ static struct ui_file *parse_gdberr;
|
||||||
|
|
||||||
static void (*kgdb_new_objfile_chain)(struct objfile * objfile);
|
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
|
static void
|
||||||
usage(void)
|
usage(void)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,19 @@ static char kvm_err[_POSIX2_LINE_MAX];
|
||||||
#define KERNOFF (kgdb_kernbase ())
|
#define KERNOFF (kgdb_kernbase ())
|
||||||
#define PINKERNEL(x) ((x) >= KERNOFF)
|
#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
|
static CORE_ADDR
|
||||||
kgdb_kernbase (void)
|
kgdb_kernbase (void)
|
||||||
{
|
{
|
||||||
|
|
@ -120,8 +133,8 @@ kgdb_trgt_open(char *filename, int from_tty)
|
||||||
|
|
||||||
old_chain = make_cleanup (xfree, filename);
|
old_chain = make_cleanup (xfree, filename);
|
||||||
|
|
||||||
nkvm = kvm_openfiles(bfd_get_filename(exec_bfd), filename, NULL,
|
nkvm = kvm_open2(bfd_get_filename(exec_bfd), filename,
|
||||||
write_files ? O_RDWR : O_RDONLY, kvm_err);
|
write_files ? O_RDWR : O_RDONLY, kvm_err, kgdb_resolve_symbol);
|
||||||
if (nkvm == NULL)
|
if (nkvm == NULL)
|
||||||
error ("Failed to open vmcore: %s", kvm_err);
|
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)
|
if (len == 0)
|
||||||
return (0);
|
return (0);
|
||||||
if (!write)
|
if (!write)
|
||||||
return (kvm_read(kvm, memaddr, myaddr, len));
|
return (kvm_read2(kvm, memaddr, myaddr, len));
|
||||||
else
|
else
|
||||||
return (kvm_write(kvm, memaddr, myaddr, len));
|
return (kvm_write(kvm, memaddr, myaddr, len));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,38 @@
|
||||||
# @(#)Makefile 8.1 (Berkeley) 6/4/93
|
# @(#)Makefile 8.1 (Berkeley) 6/4/93
|
||||||
# $FreeBSD$
|
# $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
|
LIB= kvm
|
||||||
.endif
|
|
||||||
|
|
||||||
SHLIBDIR?= /lib
|
SHLIBDIR?= /lib
|
||||||
SHLIB_MAJOR= 6
|
SHLIB_MAJOR= 6
|
||||||
CFLAGS+=-DLIBC_SCCS -I${.CURDIR}
|
CFLAGS+=-DLIBC_SCCS -I${.CURDIR}
|
||||||
|
|
||||||
.if exists(${.CURDIR}/kvm_${KVM_XARCH}.c)
|
|
||||||
KVM_ARCH=${KVM_XARCH}
|
|
||||||
.else
|
|
||||||
KVM_ARCH=${KVM_XCPUARCH}
|
|
||||||
.endif
|
|
||||||
|
|
||||||
WARNS?= 3
|
WARNS?= 3
|
||||||
|
|
||||||
SRCS= kvm.c kvm_${KVM_ARCH}.c kvm_cptime.c kvm_file.c kvm_getloadavg.c \
|
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_getswapinfo.c kvm_pcpu.c kvm_proc.c kvm_vnet.c \
|
||||||
.if exists(${.CURDIR}/kvm_minidump_${KVM_ARCH}.c)
|
kvm_minidump_aarch64.c \
|
||||||
SRCS+= kvm_minidump_${KVM_ARCH}.c
|
kvm_amd64.c kvm_minidump_amd64.c \
|
||||||
.endif
|
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
|
INCS= kvm.h
|
||||||
|
|
||||||
|
LIBADD= elf
|
||||||
|
|
||||||
MAN= kvm.3 kvm_getcptime.3 kvm_geterr.3 kvm_getfiles.3 kvm_getloadavg.3 \
|
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_getpcpu.3 kvm_getprocs.3 kvm_getswapinfo.3 kvm_native.3 \
|
||||||
kvm_read.3
|
kvm_nlist.3 kvm_open.3 kvm_read.3
|
||||||
|
|
||||||
MLINKS+=kvm_getpcpu.3 kvm_getmaxcpu.3 \
|
MLINKS+=kvm_getpcpu.3 kvm_getmaxcpu.3 \
|
||||||
kvm_getpcpu.3 kvm_dpcpu_setcpu.3 \
|
kvm_getpcpu.3 kvm_dpcpu_setcpu.3 \
|
||||||
kvm_getpcpu.3 kvm_read_zpcpu.3 \
|
kvm_getpcpu.3 kvm_read_zpcpu.3 \
|
||||||
kvm_getpcpu.3 kvm_counter_u64_fetch.3
|
kvm_getpcpu.3 kvm_counter_u64_fetch.3
|
||||||
MLINKS+=kvm_getprocs.3 kvm_getargv.3 kvm_getprocs.3 kvm_getenvv.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_nlist.3 kvm_nlist2.3
|
||||||
MLINKS+=kvm_read.3 kvm_write.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>
|
.include <bsd.lib.mk>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ DIRDEPS = \
|
||||||
lib/${CSU_DIR} \
|
lib/${CSU_DIR} \
|
||||||
lib/libc \
|
lib/libc \
|
||||||
lib/libcompiler_rt \
|
lib/libcompiler_rt \
|
||||||
|
lib/libelf \
|
||||||
|
|
||||||
|
|
||||||
.include <dirdeps.mk>
|
.include <dirdeps.mk>
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
.\" @(#)kvm.3 8.1 (Berkeley) 6/4/93
|
.\" @(#)kvm.3 8.1 (Berkeley) 6/4/93
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd April 25, 2010
|
.Dd November 27, 2015
|
||||||
.Dt KVM 3
|
.Dt KVM 3
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
|
@ -101,6 +101,44 @@ routine,
|
||||||
to return (not print out) the error message
|
to return (not print out) the error message
|
||||||
corresponding to the most recent error condition on the
|
corresponding to the most recent error condition on the
|
||||||
given descriptor.
|
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
|
.Sh SEE ALSO
|
||||||
.Xr kvm_close 3 ,
|
.Xr kvm_close 3 ,
|
||||||
.Xr kvm_getargv 3 ,
|
.Xr kvm_getargv 3 ,
|
||||||
|
|
@ -110,10 +148,14 @@ given descriptor.
|
||||||
.Xr kvm_getloadavg 3 ,
|
.Xr kvm_getloadavg 3 ,
|
||||||
.Xr kvm_getprocs 3 ,
|
.Xr kvm_getprocs 3 ,
|
||||||
.Xr kvm_getswapinfo 3 ,
|
.Xr kvm_getswapinfo 3 ,
|
||||||
|
.Xr kvm_native 3 ,
|
||||||
.Xr kvm_nlist 3 ,
|
.Xr kvm_nlist 3 ,
|
||||||
|
.Xr kvm_nlist2 3 ,
|
||||||
.Xr kvm_open 3 ,
|
.Xr kvm_open 3 ,
|
||||||
|
.Xr kvm_open2 3 ,
|
||||||
.Xr kvm_openfiles 3 ,
|
.Xr kvm_openfiles 3 ,
|
||||||
.Xr kvm_read 3 ,
|
.Xr kvm_read 3 ,
|
||||||
|
.Xr kvm_read2 3 ,
|
||||||
.Xr kvm_write 3 ,
|
.Xr kvm_write 3 ,
|
||||||
.Xr sysctl 3 ,
|
.Xr sysctl 3 ,
|
||||||
.Xr kmem 4 ,
|
.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 */
|
#endif /* LIBC_SCCS and not lint */
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
#include <sys/fnv_hash.h>
|
||||||
|
|
||||||
#define _WANT_VNET
|
#define _WANT_VNET
|
||||||
|
|
||||||
#include <sys/user.h>
|
#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/linker.h>
|
||||||
#include <sys/pcpu.h>
|
#include <sys/pcpu.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <net/vnet.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 <fcntl.h>
|
||||||
#include <kvm.h>
|
#include <kvm.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <nlist.h>
|
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <strings.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "kvm_private.h"
|
#include "kvm_private.h"
|
||||||
|
|
||||||
#ifndef CROSS_LIBKVM
|
SET_DECLARE(kvm_arch, struct kvm_arch);
|
||||||
|
|
||||||
/* from src/lib/libc/gen/nlist.c */
|
/* from src/lib/libc/gen/nlist.c */
|
||||||
int __fdnlist(int, struct nlist *);
|
int __fdnlist(int, struct nlist *);
|
||||||
|
|
||||||
#define kvm_fdnlist __fdnlist
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include <proc_service.h>
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
kvm_fdnlist(int fd, struct nlist *list)
|
kvm_fdnlist(kvm_t *kd, struct kvm_nlist *list)
|
||||||
{
|
{
|
||||||
psaddr_t addr;
|
kvaddr_t addr;
|
||||||
ps_err_e pserr;
|
int error, nfail;
|
||||||
int 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;
|
nfail = 0;
|
||||||
while (list->n_name != NULL && list->n_name[0] != '\0') {
|
while (list->n_name != NULL && list->n_name[0] != '\0') {
|
||||||
list->n_other = 0;
|
error = kd->resolve_symbol(list->n_name, &addr);
|
||||||
list->n_desc = 0;
|
if (error != 0) {
|
||||||
pserr = ps_pglobal_lookup(NULL, NULL, list->n_name, &addr);
|
|
||||||
if (pserr != PS_OK) {
|
|
||||||
nfail++;
|
nfail++;
|
||||||
list->n_value = 0;
|
list->n_value = 0;
|
||||||
list->n_type = 0;
|
list->n_type = 0;
|
||||||
|
|
@ -109,8 +110,6 @@ kvm_fdnlist(int fd, struct nlist *list)
|
||||||
return (nfail);
|
return (nfail);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CROSS_LIBKVM */
|
|
||||||
|
|
||||||
char *
|
char *
|
||||||
kvm_geterr(kvm_t *kd)
|
kvm_geterr(kvm_t *kd)
|
||||||
{
|
{
|
||||||
|
|
@ -175,9 +174,206 @@ _kvm_malloc(kvm_t *kd, size_t n)
|
||||||
return (p);
|
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 *
|
static kvm_t *
|
||||||
_kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout)
|
_kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout)
|
||||||
{
|
{
|
||||||
|
struct kvm_arch **parch;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
kd->vmfd = -1;
|
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.
|
* This is a crash dump.
|
||||||
* Initialize the virtual address translation machinery,
|
* Open the namelist fd and determine the architecture.
|
||||||
* but first setup the namelist fd.
|
|
||||||
*/
|
*/
|
||||||
if ((kd->nlfd = open(uf, O_RDONLY | O_CLOEXEC, 0)) < 0) {
|
if ((kd->nlfd = open(uf, O_RDONLY | O_CLOEXEC, 0)) < 0) {
|
||||||
_kvm_syserr(kd, kd->program, "%s", uf);
|
_kvm_syserr(kd, kd->program, "%s", uf);
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
if (_kvm_read_kernel_ehdr(kd) < 0)
|
||||||
|
goto failed;
|
||||||
if (strncmp(mf, _PATH_FWMEM, strlen(_PATH_FWMEM)) == 0)
|
if (strncmp(mf, _PATH_FWMEM, strlen(_PATH_FWMEM)) == 0)
|
||||||
kd->rawdump = 1;
|
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;
|
goto failed;
|
||||||
return (kd);
|
return (kd);
|
||||||
failed:
|
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);
|
(void)strlcpy(errout, strerror(errno), _POSIX2_LINE_MAX);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
kd->program = 0;
|
|
||||||
return (_kvm_open(kd, uf, mf, flag, errout));
|
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));
|
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
|
int
|
||||||
kvm_close(kvm_t *kd)
|
kvm_close(kvm_t *kd)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
|
if (kd->vmst != NULL)
|
||||||
|
kd->arch->ka_freevtop(kd);
|
||||||
if (kd->pmfd >= 0)
|
if (kd->pmfd >= 0)
|
||||||
error |= close(kd->pmfd);
|
error |= close(kd->pmfd);
|
||||||
if (kd->vmfd >= 0)
|
if (kd->vmfd >= 0)
|
||||||
error |= close(kd->vmfd);
|
error |= close(kd->vmfd);
|
||||||
if (kd->nlfd >= 0)
|
if (kd->nlfd >= 0)
|
||||||
error |= close(kd->nlfd);
|
error |= close(kd->nlfd);
|
||||||
if (kd->vmst)
|
|
||||||
_kvm_freevtop(kd);
|
|
||||||
if (kd->procbase != 0)
|
if (kd->procbase != 0)
|
||||||
free((void *)kd->procbase);
|
free((void *)kd->procbase);
|
||||||
if (kd->argbuf != 0)
|
if (kd->argbuf != 0)
|
||||||
|
|
@ -318,10 +551,10 @@ kvm_close(kvm_t *kd)
|
||||||
* symbol names, try again, and merge back what we could resolve.
|
* symbol names, try again, and merge back what we could resolve.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
kvm_fdnlist_prefix(kvm_t *kd, struct nlist *nl, int missing, const char *prefix,
|
kvm_fdnlist_prefix(kvm_t *kd, struct kvm_nlist *nl, int missing,
|
||||||
uintptr_t (*validate_fn)(kvm_t *, uintptr_t))
|
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;
|
char *cp, *ce;
|
||||||
const char *ccp;
|
const char *ccp;
|
||||||
size_t len;
|
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) {
|
for (p = nl; p->n_name && p->n_name[0]; ++p) {
|
||||||
if (p->n_type != N_UNDF)
|
if (p->n_type != N_UNDF)
|
||||||
continue;
|
continue;
|
||||||
len += sizeof(struct nlist) + strlen(prefix) +
|
len += sizeof(struct kvm_nlist) + strlen(prefix) +
|
||||||
2 * (strlen(p->n_name) + 1);
|
2 * (strlen(p->n_name) + 1);
|
||||||
unresolved++;
|
unresolved++;
|
||||||
}
|
}
|
||||||
if (unresolved == 0)
|
if (unresolved == 0)
|
||||||
return (unresolved);
|
return (unresolved);
|
||||||
/* Add space for the terminating nlist entry. */
|
/* Add space for the terminating nlist entry. */
|
||||||
len += sizeof(struct nlist);
|
len += sizeof(struct kvm_nlist);
|
||||||
unresolved++;
|
unresolved++;
|
||||||
|
|
||||||
/* Alloc one chunk for (nlist, [names]) and setup pointers. */
|
/* 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)
|
if (n == NULL)
|
||||||
return (missing);
|
return (missing);
|
||||||
cp = ce = (char *)np;
|
cp = ce = (char *)np;
|
||||||
cp += unresolved * sizeof(struct nlist);
|
cp += unresolved * sizeof(struct kvm_nlist);
|
||||||
ce += len;
|
ce += len;
|
||||||
|
|
||||||
/* Generate shortened nlist with special prefix. */
|
/* 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) {
|
for (p = nl; p->n_name && p->n_name[0]; ++p) {
|
||||||
if (p->n_type != N_UNDF)
|
if (p->n_type != N_UNDF)
|
||||||
continue;
|
continue;
|
||||||
bcopy(p, np, sizeof(struct nlist));
|
*np = *p;
|
||||||
/* Save the new\0orig. name so we can later match it again. */
|
/* Save the new\0orig. name so we can later match it again. */
|
||||||
slen = snprintf(cp, ce - cp, "%s%s%c%s", prefix,
|
slen = snprintf(cp, ce - cp, "%s%s%c%s", prefix,
|
||||||
(prefix[0] != '\0' && p->n_name[0] == '_') ?
|
(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. */
|
/* Do lookup on the reduced list. */
|
||||||
np = n;
|
np = n;
|
||||||
unresolved = kvm_fdnlist(kd->nlfd, np);
|
unresolved = kvm_fdnlist(kd, np);
|
||||||
|
|
||||||
/* Check if we could resolve further symbols and update the list. */
|
/* Check if we could resolve further symbols and update the list. */
|
||||||
if (unresolved >= 0 && unresolved < missing) {
|
if (unresolved >= 0 && unresolved < missing) {
|
||||||
|
|
@ -398,8 +631,6 @@ kvm_fdnlist_prefix(kvm_t *kd, struct nlist *nl, int missing, const char *prefix,
|
||||||
continue;
|
continue;
|
||||||
/* Update nlist with new, translated results. */
|
/* Update nlist with new, translated results. */
|
||||||
p->n_type = np->n_type;
|
p->n_type = np->n_type;
|
||||||
p->n_other = np->n_other;
|
|
||||||
p->n_desc = np->n_desc;
|
|
||||||
if (validate_fn)
|
if (validate_fn)
|
||||||
p->n_value = (*validate_fn)(kd, np->n_value);
|
p->n_value = (*validate_fn)(kd, np->n_value);
|
||||||
else
|
else
|
||||||
|
|
@ -418,9 +649,9 @@ kvm_fdnlist_prefix(kvm_t *kd, struct nlist *nl, int missing, const char *prefix,
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
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;
|
int nvalid;
|
||||||
struct kld_sym_lookup lookup;
|
struct kld_sym_lookup lookup;
|
||||||
int error;
|
int error;
|
||||||
|
|
@ -433,7 +664,7 @@ _kvm_nlist(kvm_t *kd, struct nlist *nl, int initialize)
|
||||||
* slow library call.
|
* slow library call.
|
||||||
*/
|
*/
|
||||||
if (!ISALIVE(kd)) {
|
if (!ISALIVE(kd)) {
|
||||||
error = kvm_fdnlist(kd->nlfd, nl);
|
error = kvm_fdnlist(kd, nl);
|
||||||
if (error <= 0) /* Hard error or success. */
|
if (error <= 0) /* Hard error or success. */
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
|
|
@ -475,8 +706,6 @@ again:
|
||||||
|
|
||||||
if (kldsym(0, KLDSYM_LOOKUP, &lookup) != -1) {
|
if (kldsym(0, KLDSYM_LOOKUP, &lookup) != -1) {
|
||||||
p->n_type = N_TEXT;
|
p->n_type = N_TEXT;
|
||||||
p->n_other = 0;
|
|
||||||
p->n_desc = 0;
|
|
||||||
if (_kvm_vnet_initialized(kd, initialize) &&
|
if (_kvm_vnet_initialized(kd, initialize) &&
|
||||||
strcmp(prefix, VNET_SYMPREFIX) == 0)
|
strcmp(prefix, VNET_SYMPREFIX) == 0)
|
||||||
p->n_value =
|
p->n_value =
|
||||||
|
|
@ -519,7 +748,7 @@ again:
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
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));
|
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
|
ssize_t
|
||||||
kvm_read(kvm_t *kd, u_long kva, void *buf, size_t len)
|
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;
|
int cc;
|
||||||
ssize_t cr;
|
ssize_t cr;
|
||||||
|
|
@ -544,7 +813,8 @@ kvm_read(kvm_t *kd, u_long kva, void *buf, size_t len)
|
||||||
*/
|
*/
|
||||||
errno = 0;
|
errno = 0;
|
||||||
if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && 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);
|
return (-1);
|
||||||
}
|
}
|
||||||
cr = read(kd->vmfd, buf, len);
|
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;
|
cp = buf;
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
cc = _kvm_kvatop(kd, kva, &pa);
|
cc = kd->arch->ka_kvatop(kd, kva, &pa);
|
||||||
if (cc == 0)
|
if (cc == 0)
|
||||||
return (-1);
|
return (-1);
|
||||||
if (cc > (ssize_t)len)
|
if (cc > (ssize_t)len)
|
||||||
|
|
@ -574,7 +844,7 @@ kvm_read(kvm_t *kd, u_long kva, void *buf, size_t len)
|
||||||
break;
|
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
|
* truncated, we might wind up seeking beyond the end of the
|
||||||
* core file in which case the read will return 0 (EOF).
|
* 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 */
|
/* 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
|
#define _SSIZE_T_DECLARED
|
||||||
#endif
|
#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;
|
typedef struct __kvm kvm_t;
|
||||||
|
|
||||||
struct kinfo_proc;
|
struct kinfo_proc;
|
||||||
|
|
@ -83,13 +91,19 @@ uint64_t kvm_counter_u64_fetch(kvm_t *, u_long);
|
||||||
struct kinfo_proc *
|
struct kinfo_proc *
|
||||||
kvm_getprocs(kvm_t *, int, int, int *);
|
kvm_getprocs(kvm_t *, int, int, int *);
|
||||||
int kvm_getswapinfo(kvm_t *, struct kvm_swap *, 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_nlist(kvm_t *, struct nlist *);
|
||||||
|
int kvm_nlist2(kvm_t *, struct kvm_nlist *);
|
||||||
kvm_t *kvm_open
|
kvm_t *kvm_open
|
||||||
(const char *, const char *, const char *, int, const char *);
|
(const char *, const char *, const char *, int, const char *);
|
||||||
kvm_t *kvm_openfiles
|
kvm_t *kvm_openfiles
|
||||||
(const char *, const char *, const char *, int, char *);
|
(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(kvm_t *, unsigned long, void *, size_t);
|
||||||
ssize_t kvm_read_zpcpu(kvm_t *, unsigned long, void *, size_t, int);
|
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);
|
ssize_t kvm_write(kvm_t *, unsigned long, const void *, size_t);
|
||||||
__END_DECLS
|
__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/param.h>
|
||||||
#include <sys/user.h>
|
#include <sys/endian.h>
|
||||||
#include <sys/proc.h>
|
#include <stdint.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <nlist.h>
|
|
||||||
#include <kvm.h>
|
#include <kvm.h>
|
||||||
|
|
||||||
#include <vm/vm.h>
|
|
||||||
#include <vm/vm_param.h>
|
|
||||||
|
|
||||||
#include <machine/elf.h>
|
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#include "kvm_private.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 {
|
struct vmstate {
|
||||||
int minidump; /* 1 = minidump mode */
|
size_t phnum;
|
||||||
void *mmapbase;
|
GElf_Phdr *phdr;
|
||||||
size_t mmapsize;
|
amd64_pml4e_t *PML4;
|
||||||
pml4_entry_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.
|
* Translate a physical memory address to a file-offset in the crash-dump.
|
||||||
*/
|
*/
|
||||||
static size_t
|
static size_t
|
||||||
_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs)
|
_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs)
|
||||||
{
|
{
|
||||||
Elf_Ehdr *e = kd->vmst->mmapbase;
|
struct vmstate *vm = kd->vmst;
|
||||||
Elf_Phdr *p;
|
GElf_Phdr *p;
|
||||||
int n;
|
size_t n;
|
||||||
|
|
||||||
if (kd->rawdump) {
|
if (kd->rawdump) {
|
||||||
*ofs = pa;
|
*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);
|
p = vm->phdr;
|
||||||
n = e->e_phnum;
|
n = vm->phnum;
|
||||||
while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
|
while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
|
||||||
p++, n--;
|
p++, n--;
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
return (0);
|
return (0);
|
||||||
*ofs = (pa - p->p_paddr) + p->p_offset;
|
*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
|
static void
|
||||||
_kvm_freevtop(kvm_t *kd)
|
_amd64_freevtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vm = kd->vmst;
|
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)
|
if (vm->PML4)
|
||||||
free(vm->PML4);
|
free(vm->PML4);
|
||||||
|
free(vm->phdr);
|
||||||
free(vm);
|
free(vm);
|
||||||
kd->vmst = NULL;
|
kd->vmst = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_initvtop(kvm_t *kd)
|
_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)
|
return (_kvm_probe_elf_kernel(kd, ELFCLASS64, EM_X86_64) &&
|
||||||
if (memcmp(&minihdr, "minidump", 8) == 0)
|
!_kvm_is_minidump(kd));
|
||||||
return (_kvm_minidump_initvtop(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));
|
kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
|
||||||
if (kd->vmst == 0) {
|
if (kd->vmst == 0) {
|
||||||
|
|
@ -166,19 +125,15 @@ _kvm_initvtop(kvm_t *kd)
|
||||||
kd->vmst->PML4 = 0;
|
kd->vmst->PML4 = 0;
|
||||||
|
|
||||||
if (kd->rawdump == 0) {
|
if (kd->rawdump == 0) {
|
||||||
if (_kvm_maphdrs(kd, sizeof(Elf_Ehdr)) == -1)
|
if (_kvm_read_core_phdrs(kd, &kd->vmst->phnum,
|
||||||
return (-1);
|
&kd->vmst->phdr) == -1)
|
||||||
|
|
||||||
ehdr = kd->vmst->mmapbase;
|
|
||||||
hdrsz = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum;
|
|
||||||
if (_kvm_maphdrs(kd, hdrsz) == -1)
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
nl[0].n_name = "kernbase";
|
nl[0].n_name = "kernbase";
|
||||||
nl[1].n_name = 0;
|
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");
|
_kvm_err(kd, kd->program, "bad namelist - no kernbase");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
@ -187,17 +142,18 @@ _kvm_initvtop(kvm_t *kd)
|
||||||
nl[0].n_name = "KPML4phys";
|
nl[0].n_name = "KPML4phys";
|
||||||
nl[1].n_name = 0;
|
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");
|
_kvm_err(kd, kd->program, "bad namelist - no KPML4phys");
|
||||||
return (-1);
|
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)) {
|
sizeof(pa)) {
|
||||||
_kvm_err(kd, kd->program, "cannot read KPML4phys");
|
_kvm_err(kd, kd->program, "cannot read KPML4phys");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
PML4 = _kvm_malloc(kd, PAGE_SIZE);
|
pa = le64toh(pa);
|
||||||
if (kvm_read(kd, pa, PML4, PAGE_SIZE) != PAGE_SIZE) {
|
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");
|
_kvm_err(kd, kd->program, "cannot read KPML4phys");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
@ -206,27 +162,27 @@ _kvm_initvtop(kvm_t *kd)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct vmstate *vm;
|
||||||
u_long offset;
|
amd64_physaddr_t offset;
|
||||||
u_long pdpe_pa;
|
amd64_physaddr_t pdpe_pa;
|
||||||
u_long pde_pa;
|
amd64_physaddr_t pde_pa;
|
||||||
u_long pte_pa;
|
amd64_physaddr_t pte_pa;
|
||||||
pml4_entry_t pml4e;
|
amd64_pml4e_t pml4e;
|
||||||
pdp_entry_t pdpe;
|
amd64_pdpe_t pdpe;
|
||||||
pd_entry_t pde;
|
amd64_pde_t pde;
|
||||||
pt_entry_t pte;
|
amd64_pte_t pte;
|
||||||
u_long pml4eindex;
|
kvaddr_t pml4eindex;
|
||||||
u_long pdpeindex;
|
kvaddr_t pdpeindex;
|
||||||
u_long pdeindex;
|
kvaddr_t pdeindex;
|
||||||
u_long pteindex;
|
kvaddr_t pteindex;
|
||||||
u_long a;
|
amd64_physaddr_t a;
|
||||||
off_t ofs;
|
off_t ofs;
|
||||||
size_t s;
|
size_t s;
|
||||||
|
|
||||||
vm = kd->vmst;
|
vm = kd->vmst;
|
||||||
offset = va & (PAGE_SIZE - 1);
|
offset = va & AMD64_PAGE_MASK;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are initializing (kernel page table descriptor pointer
|
* 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);
|
s = _kvm_pa2off(kd, va, pa);
|
||||||
if (s == 0) {
|
if (s == 0) {
|
||||||
_kvm_err(kd, kd->program,
|
_kvm_err(kd, kd->program,
|
||||||
"_kvm_vatop: bootstrap data not in dump");
|
"_amd64_vatop: bootstrap data not in dump");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
} else
|
} else
|
||||||
return (PAGE_SIZE - offset);
|
return (AMD64_PAGE_SIZE - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
pml4eindex = (va >> PML4SHIFT) & (NPML4EPG - 1);
|
pml4eindex = (va >> AMD64_PML4SHIFT) & (AMD64_NPML4EPG - 1);
|
||||||
pml4e = vm->PML4[pml4eindex];
|
pml4e = le64toh(vm->PML4[pml4eindex]);
|
||||||
if (((u_long)pml4e & PG_V) == 0) {
|
if ((pml4e & AMD64_PG_V) == 0) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pml4e not valid");
|
_kvm_err(kd, kd->program, "_amd64_vatop: pml4e not valid");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdpeindex = (va >> PDPSHIFT) & (NPDPEPG-1);
|
pdpeindex = (va >> AMD64_PDPSHIFT) & (AMD64_NPDPEPG - 1);
|
||||||
pdpe_pa = ((u_long)pml4e & PG_FRAME) +
|
pdpe_pa = (pml4e & AMD64_PG_FRAME) + (pdpeindex * sizeof(amd64_pdpe_t));
|
||||||
(pdpeindex * sizeof(pdp_entry_t));
|
|
||||||
|
|
||||||
s = _kvm_pa2off(kd, pdpe_pa, &ofs);
|
s = _kvm_pa2off(kd, pdpe_pa, &ofs);
|
||||||
if (s < sizeof pdpe) {
|
if (s < sizeof(pdpe)) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pdpe_pa not found");
|
_kvm_err(kd, kd->program, "_amd64_vatop: pdpe_pa not found");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
if (lseek(kd->pmfd, ofs, 0) == -1) {
|
if (pread(kd->pmfd, &pdpe, sizeof(pdpe), ofs) != sizeof(pdpe)) {
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: lseek pdpe_pa");
|
_kvm_syserr(kd, kd->program, "_amd64_vatop: read pdpe");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
if (read(kd->pmfd, &pdpe, sizeof pdpe) != sizeof pdpe) {
|
pdpe = le64toh(pdpe);
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: read pdpe");
|
if ((pdpe & AMD64_PG_V) == 0) {
|
||||||
goto invalid;
|
_kvm_err(kd, kd->program, "_amd64_vatop: pdpe not valid");
|
||||||
}
|
|
||||||
if (((u_long)pdpe & PG_V) == 0) {
|
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pdpe not valid");
|
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdeindex = (va >> PDRSHIFT) & (NPDEPG-1);
|
if (pdpe & AMD64_PG_PS) {
|
||||||
pde_pa = ((u_long)pdpe & PG_FRAME) + (pdeindex * sizeof(pd_entry_t));
|
/*
|
||||||
|
* No next-level page table; pdpe describes one 1GB page.
|
||||||
s = _kvm_pa2off(kd, pde_pa, &ofs);
|
*/
|
||||||
if (s < sizeof pde) {
|
a = (pde & AMD64_PG_1GB_FRAME) + (va & AMD64_PDPMASK);
|
||||||
_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);
|
|
||||||
s = _kvm_pa2off(kd, a, pa);
|
s = _kvm_pa2off(kd, a, pa);
|
||||||
if (s == 0) {
|
if (s == 0) {
|
||||||
_kvm_err(kd, kd->program,
|
_kvm_err(kd, kd->program,
|
||||||
"_kvm_vatop: 2MB page address not in dump");
|
"_amd64_vatop: 1GB page address not in dump");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
} else
|
} else
|
||||||
return (NBPDR - (va & PAGE2M_MASK));
|
return (AMD64_NBPDP - (va & AMD64_PDPMASK));
|
||||||
}
|
}
|
||||||
|
|
||||||
pteindex = (va >> PAGE_SHIFT) & (NPTEPG-1);
|
pdeindex = (va >> AMD64_PDRSHIFT) & (AMD64_NPDEPG - 1);
|
||||||
pte_pa = ((u_long)pde & PG_FRAME) + (pteindex * sizeof(pt_entry_t));
|
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);
|
s = _kvm_pa2off(kd, pte_pa, &ofs);
|
||||||
if (s < sizeof pte) {
|
if (s < sizeof(pte)) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte_pa not found");
|
_kvm_err(kd, kd->program, "_amd64_vatop: pte_pa not found");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
if (lseek(kd->pmfd, ofs, 0) == -1) {
|
if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: lseek");
|
_kvm_syserr(kd, kd->program, "_amd64_vatop: read");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
if (read(kd->pmfd, &pte, sizeof pte) != sizeof pte) {
|
if ((pte & AMD64_PG_V) == 0) {
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: read");
|
_kvm_err(kd, kd->program, "_amd64_vatop: pte not valid");
|
||||||
goto invalid;
|
|
||||||
}
|
|
||||||
if (((u_long)pte & PG_V) == 0) {
|
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
|
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
a = ((u_long)pte & PG_FRAME) + offset;
|
a = (pte & AMD64_PG_FRAME) + offset;
|
||||||
s = _kvm_pa2off(kd, a, pa);
|
s = _kvm_pa2off(kd, a, pa);
|
||||||
if (s == 0) {
|
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;
|
goto invalid;
|
||||||
} else
|
} else
|
||||||
return (PAGE_SIZE - offset);
|
return (AMD64_PAGE_SIZE - offset);
|
||||||
|
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
_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)) {
|
if (ISALIVE(kd)) {
|
||||||
_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
|
_kvm_err(kd, 0, "kvm_kvatop called in live kernel!");
|
||||||
return (0);
|
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$");
|
__FBSDID("$FreeBSD$");
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/elf32.h>
|
#include <sys/endian.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 <kvm.h>
|
#include <kvm.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef __arm__
|
||||||
|
#include <machine/vmparam.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "kvm_private.h"
|
#include "kvm_private.h"
|
||||||
|
#include "kvm_arm.h"
|
||||||
|
|
||||||
/* minidump must be the first item! */
|
|
||||||
struct vmstate {
|
struct vmstate {
|
||||||
int minidump; /* 1 = minidump mode */
|
arm_pd_entry_t *l1pt;
|
||||||
pd_entry_t *l1pt;
|
size_t phnum;
|
||||||
void *mmapbase;
|
GElf_Phdr *phdr;
|
||||||
size_t mmapsize;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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.
|
* Translate a physical memory address to a file-offset in the crash-dump.
|
||||||
*/
|
*/
|
||||||
static size_t
|
static size_t
|
||||||
_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs, size_t pgsz)
|
_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs, size_t pgsz)
|
||||||
{
|
{
|
||||||
Elf32_Ehdr *e = kd->vmst->mmapbase;
|
struct vmstate *vm = kd->vmst;
|
||||||
Elf32_Phdr *p = (Elf32_Phdr*)((char*)e + e->e_phoff);
|
GElf_Phdr *p;
|
||||||
int n = e->e_phnum;
|
size_t n;
|
||||||
|
|
||||||
|
p = vm->phdr;
|
||||||
|
n = vm->phnum;
|
||||||
while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
|
while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
|
||||||
p++, n--;
|
p++, n--;
|
||||||
if (n == 0)
|
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)));
|
return (pgsz - ((size_t)pa & (pgsz - 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
_kvm_freevtop(kvm_t *kd)
|
_arm_freevtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
if (kd->vmst != 0) {
|
struct vmstate *vm = kd->vmst;
|
||||||
if (kd->vmst->minidump)
|
|
||||||
return (_kvm_minidump_freevtop(kd));
|
free(vm->phdr);
|
||||||
if (kd->vmst->mmapbase != NULL)
|
free(vm);
|
||||||
munmap(kd->vmst->mmapbase, kd->vmst->mmapsize);
|
kd->vmst = NULL;
|
||||||
free(kd->vmst);
|
|
||||||
kd->vmst = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_initvtop(kvm_t *kd)
|
_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 vmstate *vm;
|
||||||
struct nlist nl[2];
|
struct kvm_nlist nl[2];
|
||||||
u_long kernbase, physaddr, pa;
|
kvaddr_t kernbase;
|
||||||
pd_entry_t *l1pt;
|
arm_physaddr_t physaddr, pa;
|
||||||
Elf32_Ehdr *ehdr;
|
arm_pd_entry_t *l1pt;
|
||||||
Elf32_Phdr *phdr;
|
size_t i;
|
||||||
size_t hdrsz;
|
int found;
|
||||||
char minihdr[8];
|
|
||||||
int found, i;
|
|
||||||
|
|
||||||
if (!kd->rawdump) {
|
if (kd->rawdump) {
|
||||||
if (pread(kd->pmfd, &minihdr, 8, 0) == 8) {
|
_kvm_err(kd, kd->program, "raw dumps not supported on arm");
|
||||||
if (memcmp(&minihdr, "minidump", 8) == 0)
|
return (-1);
|
||||||
return (_kvm_minidump_initvtop(kd));
|
|
||||||
} else {
|
|
||||||
_kvm_err(kd, kd->program, "cannot read header");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vm = _kvm_malloc(kd, sizeof(*vm));
|
vm = _kvm_malloc(kd, sizeof(*vm));
|
||||||
|
|
@ -154,19 +123,15 @@ _kvm_initvtop(kvm_t *kd)
|
||||||
}
|
}
|
||||||
kd->vmst = vm;
|
kd->vmst = vm;
|
||||||
vm->l1pt = NULL;
|
vm->l1pt = NULL;
|
||||||
if (_kvm_maphdrs(kd, sizeof(Elf32_Ehdr)) == -1)
|
|
||||||
return (-1);
|
if (_kvm_read_core_phdrs(kd, &vm->phnum, &vm->phdr) == -1)
|
||||||
ehdr = kd->vmst->mmapbase;
|
|
||||||
hdrsz = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum;
|
|
||||||
if (_kvm_maphdrs(kd, hdrsz) == -1)
|
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
phdr = (Elf32_Phdr *)((uint8_t *)ehdr + ehdr->e_phoff);
|
|
||||||
found = 0;
|
found = 0;
|
||||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
for (i = 0; i < vm->phnum; i++) {
|
||||||
if (phdr[i].p_type == PT_DUMP_DELTA) {
|
if (vm->phdr[i].p_type == PT_DUMP_DELTA) {
|
||||||
kernbase = phdr[i].p_vaddr;
|
kernbase = vm->phdr[i].p_vaddr;
|
||||||
physaddr = phdr[i].p_paddr;
|
physaddr = vm->phdr[i].p_paddr;
|
||||||
found = 1;
|
found = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -175,30 +140,35 @@ _kvm_initvtop(kvm_t *kd)
|
||||||
nl[1].n_name = NULL;
|
nl[1].n_name = NULL;
|
||||||
if (!found) {
|
if (!found) {
|
||||||
nl[0].n_name = "kernbase";
|
nl[0].n_name = "kernbase";
|
||||||
if (kvm_nlist(kd, nl) != 0)
|
if (kvm_nlist2(kd, nl) != 0) {
|
||||||
|
#ifdef __arm__
|
||||||
kernbase = KERNBASE;
|
kernbase = KERNBASE;
|
||||||
else
|
#else
|
||||||
|
_kvm_err(kd, kd->program, "cannot resolve kernbase");
|
||||||
|
return (-1);
|
||||||
|
#endif
|
||||||
|
} else
|
||||||
kernbase = nl[0].n_value;
|
kernbase = nl[0].n_value;
|
||||||
|
|
||||||
nl[0].n_name = "physaddr";
|
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");
|
_kvm_err(kd, kd->program, "couldn't get phys addr");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
physaddr = nl[0].n_value;
|
physaddr = nl[0].n_value;
|
||||||
}
|
}
|
||||||
nl[0].n_name = "kernel_l1pa";
|
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");
|
_kvm_err(kd, kd->program, "bad namelist");
|
||||||
return (-1);
|
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)) {
|
sizeof(pa)) != sizeof(pa)) {
|
||||||
_kvm_err(kd, kd->program, "cannot read kernel_l1pa");
|
_kvm_err(kd, kd->program, "cannot read kernel_l1pa");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
l1pt = _kvm_malloc(kd, L1_TABLE_SIZE);
|
l1pt = _kvm_malloc(kd, ARM_L1_TABLE_SIZE);
|
||||||
if (kvm_read(kd, pa, l1pt, L1_TABLE_SIZE) != 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");
|
_kvm_err(kd, kd->program, "cannot read l1pt");
|
||||||
free(l1pt);
|
free(l1pt);
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
@ -208,62 +178,51 @@ _kvm_initvtop(kvm_t *kd)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* from arm/pmap.c */
|
/* from arm/pmap.c */
|
||||||
#define L1_IDX(va) (((vm_offset_t)(va)) >> L1_S_SHIFT)
|
#define ARM_L1_IDX(va) ((va) >> ARM_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 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 l1pte_valid(pde) ((pde) != 0)
|
||||||
#define l2pte_valid(pte) ((pte) != 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
|
static int
|
||||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
_arm_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||||
{
|
{
|
||||||
struct vmstate *vm = kd->vmst;
|
struct vmstate *vm = kd->vmst;
|
||||||
pd_entry_t pd;
|
arm_pd_entry_t pd;
|
||||||
pt_entry_t pte;
|
arm_pt_entry_t pte;
|
||||||
off_t pte_pa;
|
arm_physaddr_t pte_pa;
|
||||||
|
off_t pte_off;
|
||||||
if (kd->vmst->minidump)
|
|
||||||
return (_kvm_minidump_kvatop(kd, va, pa));
|
|
||||||
|
|
||||||
if (vm->l1pt == NULL)
|
if (vm->l1pt == NULL)
|
||||||
return (_kvm_pa2off(kd, va, pa, PAGE_SIZE));
|
return (_kvm_pa2off(kd, va, pa, ARM_PAGE_SIZE));
|
||||||
pd = vm->l1pt[L1_IDX(va)];
|
pd = _kvm32toh(kd, vm->l1pt[ARM_L1_IDX(va)]);
|
||||||
if (!l1pte_valid(pd))
|
if (!l1pte_valid(pd))
|
||||||
goto invalid;
|
goto invalid;
|
||||||
if (l1pte_section_p(pd)) {
|
if (l1pte_section_p(pd)) {
|
||||||
/* 1MB section mapping. */
|
/* 1MB section mapping. */
|
||||||
*pa = ((u_long)pd & L1_S_ADDR_MASK) + (va & L1_S_OFFSET);
|
*pa = (pd & ARM_L1_S_ADDR_MASK) + (va & ARM_L1_S_OFFSET);
|
||||||
return (_kvm_pa2off(kd, *pa, pa, L1_S_SIZE));
|
return (_kvm_pa2off(kd, *pa, pa, ARM_L1_S_SIZE));
|
||||||
}
|
}
|
||||||
pte_pa = (pd & L1_ADDR_MASK) + l2pte_index(va) * sizeof(pte);
|
pte_pa = (pd & ARM_L1_C_ADDR_MASK) + l2pte_index(va) * sizeof(pte);
|
||||||
_kvm_pa2off(kd, pte_pa, &pte_pa, L1_S_SIZE);
|
_kvm_pa2off(kd, pte_pa, &pte_off, ARM_L1_S_SIZE);
|
||||||
if (lseek(kd->pmfd, pte_pa, 0) == -1) {
|
if (pread(kd->pmfd, &pte, sizeof(pte), pte_off) != sizeof(pte)) {
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_kvatop: lseek");
|
_kvm_syserr(kd, kd->program, "_arm_kvatop: pread");
|
||||||
goto invalid;
|
|
||||||
}
|
|
||||||
if (read(kd->pmfd, &pte, sizeof(pte)) != sizeof (pte)) {
|
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_kvatop: read");
|
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
pte = _kvm32toh(kd, pte);
|
||||||
if (!l2pte_valid(pte)) {
|
if (!l2pte_valid(pte)) {
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
if ((pte & L2_TYPE_MASK) == L2_TYPE_L) {
|
if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_L) {
|
||||||
*pa = (pte & L2_L_FRAME) | (va & L2_L_OFFSET);
|
*pa = (pte & ARM_L2_L_FRAME) | (va & ARM_L2_L_OFFSET);
|
||||||
return (_kvm_pa2off(kd, *pa, pa, L2_L_SIZE));
|
return (_kvm_pa2off(kd, *pa, pa, ARM_L2_L_SIZE));
|
||||||
}
|
}
|
||||||
*pa = (pte & L2_S_FRAME) | (va & L2_S_OFFSET);
|
*pa = (pte & ARM_L2_S_FRAME) | (va & ARM_L2_S_OFFSET);
|
||||||
return (_kvm_pa2off(kd, *pa, pa, PAGE_SIZE));
|
return (_kvm_pa2off(kd, *pa, pa, ARM_PAGE_SIZE));
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "Invalid address (%lx)", va);
|
_kvm_err(kd, 0, "Invalid address (%jx)", (uintmax_t)va);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -284,3 +243,28 @@ _kvm_mdopen(kvm_t *kd)
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
#endif
|
#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) *
|
return (getsysctl(kd, "kern.cp_time", cp_time, sizeof(long) *
|
||||||
CPUSTATES));
|
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_cached == 0) {
|
||||||
if (_kvm_cp_time_init(kd) < 0)
|
if (_kvm_cp_time_init(kd) < 0)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,12 @@ kvm_getloadavg(kvm_t *kd, double loadavg[], int nelem)
|
||||||
if (ISALIVE(kd))
|
if (ISALIVE(kd))
|
||||||
return (getloadavg(loadavg, nelem));
|
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) {
|
if (kvm_nlist(kd, nl) != 0) {
|
||||||
for (p = nl; p->n_type != 0; ++p);
|
for (p = nl; p->n_type != 0; ++p);
|
||||||
_kvm_err(kd, kd->program,
|
_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 swdevt *sp, swinfo;
|
||||||
struct kvm_swap tot;
|
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))
|
if (!nlist_init(kd))
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,171 +46,133 @@ static char sccsid[] = "@(#)kvm_hp300.c 8.1 (Berkeley) 6/4/93";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/user.h>
|
#include <sys/endian.h>
|
||||||
#include <sys/proc.h>
|
#include <stdint.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <nlist.h>
|
|
||||||
#include <kvm.h>
|
#include <kvm.h>
|
||||||
|
|
||||||
#include <vm/vm.h>
|
#ifdef __i386__
|
||||||
#include <vm/vm_param.h>
|
#include <machine/vmparam.h> /* For KERNBASE. */
|
||||||
|
#endif
|
||||||
#include <machine/elf.h>
|
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#include "kvm_private.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 {
|
struct vmstate {
|
||||||
int minidump; /* 1 = minidump mode */
|
|
||||||
void *mmapbase;
|
|
||||||
size_t mmapsize;
|
|
||||||
void *PTD;
|
void *PTD;
|
||||||
int pae;
|
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.
|
* Translate a physical memory address to a file-offset in the crash-dump.
|
||||||
*/
|
*/
|
||||||
static size_t
|
static size_t
|
||||||
_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs)
|
_kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs)
|
||||||
{
|
{
|
||||||
Elf_Ehdr *e = kd->vmst->mmapbase;
|
struct vmstate *vm = kd->vmst;
|
||||||
Elf_Phdr *p;
|
GElf_Phdr *p;
|
||||||
int n;
|
size_t n;
|
||||||
|
|
||||||
if (kd->rawdump) {
|
if (kd->rawdump) {
|
||||||
*ofs = pa;
|
*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);
|
p = vm->phdr;
|
||||||
n = e->e_phnum;
|
n = vm->phnum;
|
||||||
while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
|
while (n && (pa < p->p_paddr || pa >= p->p_paddr + p->p_memsz))
|
||||||
p++, n--;
|
p++, n--;
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
return (0);
|
return (0);
|
||||||
*ofs = (pa - p->p_paddr) + p->p_offset;
|
*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
|
static void
|
||||||
_kvm_freevtop(kvm_t *kd)
|
_i386_freevtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vm = kd->vmst;
|
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)
|
if (vm->PTD)
|
||||||
free(vm->PTD);
|
free(vm->PTD);
|
||||||
|
free(vm->phdr);
|
||||||
free(vm);
|
free(vm);
|
||||||
kd->vmst = NULL;
|
kd->vmst = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_initvtop(kvm_t *kd)
|
_i386_probe(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct nlist nl[2];
|
|
||||||
u_long pa;
|
return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_386) &&
|
||||||
u_long kernbase;
|
!_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;
|
char *PTD;
|
||||||
Elf_Ehdr *ehdr;
|
|
||||||
size_t hdrsz;
|
|
||||||
int i;
|
int i;
|
||||||
char minihdr[8];
|
|
||||||
|
|
||||||
if (!kd->rawdump && pread(kd->pmfd, &minihdr, 8, 0) == 8)
|
kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(struct vmstate));
|
||||||
if (memcmp(&minihdr, "minidump", 8) == 0)
|
if (kd->vmst == NULL) {
|
||||||
return (_kvm_minidump_initvtop(kd));
|
|
||||||
|
|
||||||
kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
|
|
||||||
if (kd->vmst == 0) {
|
|
||||||
_kvm_err(kd, kd->program, "cannot allocate vm");
|
_kvm_err(kd, kd->program, "cannot allocate vm");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
kd->vmst->PTD = 0;
|
kd->vmst->PTD = 0;
|
||||||
|
|
||||||
if (kd->rawdump == 0) {
|
if (kd->rawdump == 0) {
|
||||||
if (_kvm_maphdrs(kd, sizeof(Elf_Ehdr)) == -1)
|
if (_kvm_read_core_phdrs(kd, &kd->vmst->phnum,
|
||||||
return (-1);
|
&kd->vmst->phdr) == -1)
|
||||||
|
|
||||||
ehdr = kd->vmst->mmapbase;
|
|
||||||
hdrsz = ehdr->e_phoff + ehdr->e_phentsize * ehdr->e_phnum;
|
|
||||||
if (_kvm_maphdrs(kd, hdrsz) == -1)
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
nl[0].n_name = "kernbase";
|
nl[0].n_name = "kernbase";
|
||||||
nl[1].n_name = 0;
|
nl[1].n_name = 0;
|
||||||
|
|
||||||
if (kvm_nlist(kd, nl) != 0)
|
if (kvm_nlist2(kd, nl) != 0) {
|
||||||
|
#ifdef __i386__
|
||||||
kernbase = KERNBASE; /* for old kernels */
|
kernbase = KERNBASE; /* for old kernels */
|
||||||
else
|
#else
|
||||||
|
_kvm_err(kd, kd->program, "cannot resolve kernbase");
|
||||||
|
return (-1);
|
||||||
|
#endif
|
||||||
|
} else
|
||||||
kernbase = nl[0].n_value;
|
kernbase = nl[0].n_value;
|
||||||
|
|
||||||
nl[0].n_name = "IdlePDPT";
|
nl[0].n_name = "IdlePDPT";
|
||||||
nl[1].n_name = 0;
|
nl[1].n_name = 0;
|
||||||
|
|
||||||
if (kvm_nlist(kd, nl) == 0) {
|
if (kvm_nlist2(kd, nl) == 0) {
|
||||||
uint64_t pa64;
|
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)) {
|
sizeof(pa)) != sizeof(pa)) {
|
||||||
_kvm_err(kd, kd->program, "cannot read IdlePDPT");
|
_kvm_err(kd, kd->program, "cannot read IdlePDPT");
|
||||||
return (-1);
|
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++) {
|
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)) {
|
sizeof(pa64)) != sizeof(pa64)) {
|
||||||
_kvm_err(kd, kd->program, "Cannot read PDPT");
|
_kvm_err(kd, kd->program, "Cannot read PDPT");
|
||||||
free(PTD);
|
free(PTD);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (kvm_read(kd, pa64 & PG_FRAME_PAE,
|
pa64 = le64toh(pa64);
|
||||||
PTD + (i * PAGE_SIZE), PAGE_SIZE) != (PAGE_SIZE)) {
|
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");
|
_kvm_err(kd, kd->program, "cannot read PDPT");
|
||||||
free(PTD);
|
free(PTD);
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
@ -222,17 +184,18 @@ _kvm_initvtop(kvm_t *kd)
|
||||||
nl[0].n_name = "IdlePTD";
|
nl[0].n_name = "IdlePTD";
|
||||||
nl[1].n_name = 0;
|
nl[1].n_name = 0;
|
||||||
|
|
||||||
if (kvm_nlist(kd, nl) != 0) {
|
if (kvm_nlist2(kd, nl) != 0) {
|
||||||
_kvm_err(kd, kd->program, "bad namelist");
|
_kvm_err(kd, kd->program, "bad namelist");
|
||||||
return (-1);
|
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)) {
|
sizeof(pa)) != sizeof(pa)) {
|
||||||
_kvm_err(kd, kd->program, "cannot read IdlePTD");
|
_kvm_err(kd, kd->program, "cannot read IdlePTD");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
PTD = _kvm_malloc(kd, PAGE_SIZE);
|
pa = le32toh(pa);
|
||||||
if (kvm_read(kd, pa, PTD, PAGE_SIZE) != PAGE_SIZE) {
|
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");
|
_kvm_err(kd, kd->program, "cannot read PTD");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
@ -243,24 +206,23 @@ _kvm_initvtop(kvm_t *kd)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct vmstate *vm;
|
||||||
u_long offset;
|
i386_physaddr_t offset;
|
||||||
u_long pte_pa;
|
i386_physaddr_t pte_pa;
|
||||||
u_long pde_pa;
|
i386_pde_t pde;
|
||||||
pd_entry_t pde;
|
i386_pte_t pte;
|
||||||
pt_entry_t pte;
|
kvaddr_t pdeindex;
|
||||||
u_long pdeindex;
|
kvaddr_t pteindex;
|
||||||
u_long pteindex;
|
|
||||||
size_t s;
|
size_t s;
|
||||||
u_long a;
|
i386_physaddr_t a;
|
||||||
off_t ofs;
|
off_t ofs;
|
||||||
uint32_t *PTD;
|
i386_pde_t *PTD;
|
||||||
|
|
||||||
vm = kd->vmst;
|
vm = kd->vmst;
|
||||||
PTD = (uint32_t *)vm->PTD;
|
PTD = (i386_pde_t *)vm->PTD;
|
||||||
offset = va & (PAGE_SIZE - 1);
|
offset = va & I386_PAGE_MASK;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are initializing (kernel page table descriptor pointer
|
* 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);
|
s = _kvm_pa2off(kd, va, pa);
|
||||||
if (s == 0) {
|
if (s == 0) {
|
||||||
_kvm_err(kd, kd->program,
|
_kvm_err(kd, kd->program,
|
||||||
"_kvm_vatop: bootstrap data not in dump");
|
"_i386_vatop: bootstrap data not in dump");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
} else
|
} else
|
||||||
return (PAGE_SIZE - offset);
|
return (I386_PAGE_SIZE - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
pdeindex = va >> PDRSHIFT;
|
pdeindex = va >> I386_PDRSHIFT;
|
||||||
pde = PTD[pdeindex];
|
pde = le32toh(PTD[pdeindex]);
|
||||||
if (((u_long)pde & PG_V) == 0) {
|
if ((pde & I386_PG_V) == 0) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pde not valid");
|
_kvm_err(kd, kd->program, "_i386_vatop: pde not valid");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((u_long)pde & PG_PS) {
|
if (pde & I386_PG_PS) {
|
||||||
/*
|
/*
|
||||||
* No second-level page table; ptd describes one 4MB page.
|
* No second-level page table; ptd describes one 4MB
|
||||||
* (We assume that the kernel wouldn't set PG_PS without enabling
|
* page. (We assume that the kernel wouldn't set
|
||||||
* it cr0).
|
* PG_PS without enabling it cr0).
|
||||||
*/
|
*/
|
||||||
#define PAGE4M_MASK (NBPDR - 1)
|
offset = va & I386_PAGE_PS_MASK;
|
||||||
#define PG_FRAME4M (~PAGE4M_MASK)
|
a = (pde & I386_PG_PS_FRAME) + offset;
|
||||||
pde_pa = ((u_long)pde & PG_FRAME4M) + (va & PAGE4M_MASK);
|
s = _kvm_pa2off(kd, a, pa);
|
||||||
s = _kvm_pa2off(kd, pde_pa, &ofs);
|
|
||||||
if (s == 0) {
|
if (s == 0) {
|
||||||
_kvm_err(kd, kd->program,
|
_kvm_err(kd, kd->program,
|
||||||
"_kvm_vatop: 4MB page address not in dump");
|
"_i386_vatop: 4MB page address not in dump");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
*pa = ofs;
|
return (I386_NBPDR - offset);
|
||||||
return (NBPDR - (va & PAGE4M_MASK));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pteindex = (va >> PAGE_SHIFT) & (NPTEPG-1);
|
pteindex = (va >> I386_PAGE_SHIFT) & (I386_NPTEPG - 1);
|
||||||
pte_pa = ((u_long)pde & PG_FRAME) + (pteindex * sizeof(pde));
|
pte_pa = (pde & I386_PG_FRAME) + (pteindex * sizeof(pte));
|
||||||
|
|
||||||
s = _kvm_pa2off(kd, pte_pa, &ofs);
|
s = _kvm_pa2off(kd, pte_pa, &ofs);
|
||||||
if (s < sizeof pte) {
|
if (s < sizeof(pte)) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pdpe_pa not found");
|
_kvm_err(kd, kd->program, "_i386_vatop: pte_pa not found");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX This has to be a physical address read, kvm_read is virtual */
|
/* XXX This has to be a physical address read, kvm_read is virtual */
|
||||||
if (lseek(kd->pmfd, ofs, 0) == -1) {
|
if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: lseek");
|
_kvm_syserr(kd, kd->program, "_i386_vatop: pread");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
if (read(kd->pmfd, &pte, sizeof pte) != sizeof pte) {
|
pte = le32toh(pte);
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_vatop: read");
|
if ((pte & I386_PG_V) == 0) {
|
||||||
goto invalid;
|
|
||||||
}
|
|
||||||
if (((u_long)pte & PG_V) == 0) {
|
|
||||||
_kvm_err(kd, kd->program, "_kvm_kvatop: pte not valid");
|
_kvm_err(kd, kd->program, "_kvm_kvatop: pte not valid");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
a = ((u_long)pte & PG_FRAME) + offset;
|
a = (pte & I386_PG_FRAME) + offset;
|
||||||
s =_kvm_pa2off(kd, a, pa);
|
s = _kvm_pa2off(kd, a, pa);
|
||||||
if (s == 0) {
|
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;
|
goto invalid;
|
||||||
} else
|
} else
|
||||||
return (PAGE_SIZE - offset);
|
return (I386_PAGE_SIZE - offset);
|
||||||
|
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct vmstate *vm;
|
||||||
uint64_t offset;
|
i386_physaddr_pae_t offset;
|
||||||
uint64_t pte_pa;
|
i386_physaddr_pae_t pte_pa;
|
||||||
uint64_t pde_pa;
|
i386_pde_pae_t pde;
|
||||||
uint64_t pde;
|
i386_pte_pae_t pte;
|
||||||
uint64_t pte;
|
kvaddr_t pdeindex;
|
||||||
u_long pdeindex;
|
kvaddr_t pteindex;
|
||||||
u_long pteindex;
|
|
||||||
size_t s;
|
size_t s;
|
||||||
uint64_t a;
|
i386_physaddr_pae_t a;
|
||||||
off_t ofs;
|
off_t ofs;
|
||||||
uint64_t *PTD;
|
i386_pde_pae_t *PTD;
|
||||||
|
|
||||||
vm = kd->vmst;
|
vm = kd->vmst;
|
||||||
PTD = (uint64_t *)vm->PTD;
|
PTD = (i386_pde_pae_t *)vm->PTD;
|
||||||
offset = va & (PAGE_SIZE - 1);
|
offset = va & I386_PAGE_MASK;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are initializing (kernel page table descriptor pointer
|
* 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);
|
s = _kvm_pa2off(kd, va, pa);
|
||||||
if (s == 0) {
|
if (s == 0) {
|
||||||
_kvm_err(kd, kd->program,
|
_kvm_err(kd, kd->program,
|
||||||
"_kvm_vatop_pae: bootstrap data not in dump");
|
"_i386_vatop_pae: bootstrap data not in dump");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
} else
|
} else
|
||||||
return (PAGE_SIZE - offset);
|
return (I386_PAGE_SIZE - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
pdeindex = va >> PDRSHIFT_PAE;
|
pdeindex = va >> I386_PDRSHIFT_PAE;
|
||||||
pde = PTD[pdeindex];
|
pde = le64toh(PTD[pdeindex]);
|
||||||
if (((u_long)pde & PG_V) == 0) {
|
if ((pde & I386_PG_V) == 0) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_kvatop_pae: pde not valid");
|
_kvm_err(kd, kd->program, "_kvm_kvatop_pae: pde not valid");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((u_long)pde & PG_PS) {
|
if (pde & I386_PG_PS) {
|
||||||
/*
|
/*
|
||||||
* No second-level page table; ptd describes one 2MB page.
|
* No second-level page table; ptd describes one 2MB
|
||||||
* (We assume that the kernel wouldn't set PG_PS without enabling
|
* page. (We assume that the kernel wouldn't set
|
||||||
* it cr0).
|
* PG_PS without enabling it cr0).
|
||||||
*/
|
*/
|
||||||
#define PAGE2M_MASK (NBPDR_PAE - 1)
|
offset = va & I386_PAGE_PS_MASK_PAE;
|
||||||
#define PG_FRAME2M (~PAGE2M_MASK)
|
a = (pde & I386_PG_PS_FRAME_PAE) + offset;
|
||||||
pde_pa = ((u_long)pde & PG_FRAME2M) + (va & PAGE2M_MASK);
|
s = _kvm_pa2off(kd, a, pa);
|
||||||
s = _kvm_pa2off(kd, pde_pa, &ofs);
|
|
||||||
if (s == 0) {
|
if (s == 0) {
|
||||||
_kvm_err(kd, kd->program,
|
_kvm_err(kd, kd->program,
|
||||||
"_kvm_vatop: 2MB page address not in dump");
|
"_i386_vatop: 2MB page address not in dump");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
*pa = ofs;
|
return (I386_NBPDR_PAE - offset);
|
||||||
return (NBPDR_PAE - (va & PAGE2M_MASK));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pteindex = (va >> PAGE_SHIFT) & (NPTEPG_PAE-1);
|
pteindex = (va >> I386_PAGE_SHIFT) & (I386_NPTEPG_PAE - 1);
|
||||||
pte_pa = ((uint64_t)pde & PG_FRAME_PAE) + (pteindex * sizeof(pde));
|
pte_pa = (pde & I386_PG_FRAME_PAE) + (pteindex * sizeof(pde));
|
||||||
|
|
||||||
s = _kvm_pa2off(kd, pte_pa, &ofs);
|
s = _kvm_pa2off(kd, pte_pa, &ofs);
|
||||||
if (s < sizeof pte) {
|
if (s < sizeof(pte)) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop_pae: pdpe_pa not found");
|
_kvm_err(kd, kd->program, "_i386_vatop_pae: pdpe_pa not found");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX This has to be a physical address read, kvm_read is virtual */
|
/* XXX This has to be a physical address read, kvm_read is virtual */
|
||||||
if (lseek(kd->pmfd, ofs, 0) == -1) {
|
if (pread(kd->pmfd, &pte, sizeof(pte), ofs) != sizeof(pte)) {
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_vatop_pae: lseek");
|
_kvm_syserr(kd, kd->program, "_i386_vatop_pae: read");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
if (read(kd->pmfd, &pte, sizeof pte) != sizeof pte) {
|
pte = le64toh(pte);
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_vatop_pae: read");
|
if ((pte & I386_PG_V) == 0) {
|
||||||
goto invalid;
|
_kvm_err(kd, kd->program, "_i386_vatop_pae: pte not valid");
|
||||||
}
|
|
||||||
if (((uint64_t)pte & PG_V) == 0) {
|
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop_pae: pte not valid");
|
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
a = ((uint64_t)pte & PG_FRAME_PAE) + offset;
|
a = (pte & I386_PG_FRAME_PAE) + offset;
|
||||||
s =_kvm_pa2off(kd, a, pa);
|
s = _kvm_pa2off(kd, a, pa);
|
||||||
if (s == 0) {
|
if (s == 0) {
|
||||||
_kvm_err(kd, kd->program,
|
_kvm_err(kd, kd->program,
|
||||||
"_kvm_vatop_pae: address not in dump");
|
"_i386_vatop_pae: address not in dump");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
} else
|
} else
|
||||||
return (PAGE_SIZE - offset);
|
return (I386_PAGE_SIZE - offset);
|
||||||
|
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
_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)) {
|
if (ISALIVE(kd)) {
|
||||||
_kvm_err(kd, 0, "vatop called in live kernel!");
|
_kvm_err(kd, 0, "vatop called in live kernel!");
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
if (kd->vmst->pae)
|
if (kd->vmst->pae)
|
||||||
return (_kvm_vatop_pae(kd, va, pa));
|
return (_i386_vatop_pae(kd, va, pa));
|
||||||
else
|
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/param.h>
|
||||||
#include <sys/user.h>
|
#include <stdint.h>
|
||||||
#include <sys/proc.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/fnv_hash.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <nlist.h>
|
|
||||||
#include <kvm.h>
|
#include <kvm.h>
|
||||||
|
|
||||||
#include <vm/vm.h>
|
#include "../../sys/arm64/include/minidump.h"
|
||||||
#include <vm/vm_param.h>
|
|
||||||
|
|
||||||
#include <machine/elf.h>
|
|
||||||
#include <machine/cpufunc.h>
|
|
||||||
#include <machine/pte.h>
|
|
||||||
#include <machine/minidump.h>
|
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "kvm_private.h"
|
#include "kvm_private.h"
|
||||||
|
#include "kvm_aarch64.h"
|
||||||
|
|
||||||
struct hpte {
|
#define aarch64_round_page(x) roundup2((kvaddr_t)(x), AARCH64_PAGE_SIZE)
|
||||||
struct hpte *next;
|
|
||||||
vm_paddr_t pa;
|
|
||||||
int64_t off;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define HPT_SIZE 1024
|
|
||||||
|
|
||||||
/* minidump must be the first item! */
|
|
||||||
struct vmstate {
|
struct vmstate {
|
||||||
int minidump; /* 1 = minidump mode */
|
|
||||||
struct minidumphdr hdr;
|
struct minidumphdr hdr;
|
||||||
void *hpt_head[HPT_SIZE];
|
struct hpt hpt;
|
||||||
uint64_t *bitmap;
|
|
||||||
uint64_t *page_map;
|
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
|
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++) {
|
return (_kvm_probe_elf_kernel(kd, ELFCLASS64, EM_AARCH64) &&
|
||||||
bits = base[idx];
|
_kvm_is_minidump(kd));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
_kvm_minidump_freevtop(kvm_t *kd)
|
_aarch64_minidump_freevtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vm = kd->vmst;
|
struct vmstate *vm = kd->vmst;
|
||||||
|
|
||||||
free(vm->bitmap);
|
_kvm_hpt_free(&vm->hpt);
|
||||||
free(vm->page_map);
|
free(vm->page_map);
|
||||||
free(vm);
|
free(vm);
|
||||||
kd->vmst = NULL;
|
kd->vmst = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_minidump_initvtop(kvm_t *kd)
|
_aarch64_minidump_initvtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vmst;
|
struct vmstate *vmst;
|
||||||
|
uint64_t *bitmap;
|
||||||
off_t off;
|
off_t off;
|
||||||
|
|
||||||
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
||||||
|
|
@ -147,7 +86,6 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
kd->vmst = vmst;
|
kd->vmst = vmst;
|
||||||
vmst->minidump = 1;
|
|
||||||
if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
|
if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
|
||||||
sizeof(vmst->hdr)) {
|
sizeof(vmst->hdr)) {
|
||||||
_kvm_err(kd, kd->program, "cannot read dump header");
|
_kvm_err(kd, kd->program, "cannot read dump header");
|
||||||
|
|
@ -159,111 +97,157 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||||
return (-1);
|
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. "
|
_kvm_err(kd, kd->program, "wrong minidump version. "
|
||||||
"Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
|
"Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
|
||||||
return (-1);
|
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 */
|
/* 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);
|
bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
|
||||||
if (vmst->bitmap == NULL) {
|
if (bitmap == NULL) {
|
||||||
_kvm_err(kd, kd->program,
|
_kvm_err(kd, kd->program,
|
||||||
"cannot allocate %d bytes for bitmap",
|
"cannot allocate %d bytes for bitmap",
|
||||||
vmst->hdr.bitmapsize);
|
vmst->hdr.bitmapsize);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
|
if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
|
||||||
vmst->hdr.bitmapsize) {
|
(ssize_t)vmst->hdr.bitmapsize) {
|
||||||
_kvm_err(kd, kd->program,
|
_kvm_err(kd, kd->program,
|
||||||
"cannot read %d bytes for page bitmap",
|
"cannot read %d bytes for page bitmap",
|
||||||
vmst->hdr.bitmapsize);
|
vmst->hdr.bitmapsize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
off += round_page(vmst->hdr.bitmapsize);
|
off += aarch64_round_page(vmst->hdr.bitmapsize);
|
||||||
|
|
||||||
vmst->page_map = _kvm_malloc(kd, vmst->hdr.pmapsize);
|
vmst->page_map = _kvm_malloc(kd, vmst->hdr.pmapsize);
|
||||||
if (vmst->page_map == NULL) {
|
if (vmst->page_map == NULL) {
|
||||||
_kvm_err(kd, kd->program,
|
_kvm_err(kd, kd->program,
|
||||||
"cannot allocate %d bytes for page_map",
|
"cannot allocate %d bytes for page_map",
|
||||||
vmst->hdr.pmapsize);
|
vmst->hdr.pmapsize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
/* This is the end of the dump, savecore may have truncated it. */
|
/* 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) <
|
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",
|
_kvm_err(kd, kd->program, "cannot read %d bytes for page_map",
|
||||||
vmst->hdr.pmapsize);
|
vmst->hdr.pmapsize);
|
||||||
|
free(bitmap);
|
||||||
|
return (-1);
|
||||||
}
|
}
|
||||||
off += vmst->hdr.pmapsize;
|
off += vmst->hdr.pmapsize;
|
||||||
|
|
||||||
/* build physical address hash table for sparse pages */
|
/* 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);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct vmstate *vm;
|
||||||
u_long offset;
|
aarch64_physaddr_t offset;
|
||||||
pt_entry_t l3;
|
aarch64_pte_t l3;
|
||||||
u_long l3_index;
|
kvaddr_t l3_index;
|
||||||
u_long a;
|
aarch64_physaddr_t a;
|
||||||
off_t ofs;
|
off_t ofs;
|
||||||
|
|
||||||
vm = kd->vmst;
|
vm = kd->vmst;
|
||||||
offset = va & PAGE_MASK;
|
offset = va & AARCH64_PAGE_MASK;
|
||||||
|
|
||||||
if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
|
if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
|
||||||
a = (va - vm->hdr.dmapbase + vm->hdr.dmapphys) & ~PAGE_MASK;
|
a = (va - vm->hdr.dmapbase + vm->hdr.dmapphys) &
|
||||||
ofs = hpt_find(kd, a);
|
~AARCH64_PAGE_MASK;
|
||||||
|
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||||
if (ofs == -1) {
|
if (ofs == -1) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: "
|
_kvm_err(kd, kd->program, "_aarch64_minidump_vatop: "
|
||||||
"direct map address 0x%lx not in minidump", va);
|
"direct map address 0x%jx not in minidump",
|
||||||
|
(uintmax_t)va);
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
*pa = ofs + offset;
|
*pa = ofs + offset;
|
||||||
return (PAGE_SIZE - offset);
|
return (AARCH64_PAGE_SIZE - offset);
|
||||||
} else if (va >= vm->hdr.kernbase) {
|
} 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))
|
if (l3_index >= vm->hdr.pmapsize / sizeof(*vm->page_map))
|
||||||
goto invalid;
|
goto invalid;
|
||||||
l3 = vm->page_map[l3_index];
|
l3 = le64toh(vm->page_map[l3_index]);
|
||||||
if ((l3 & ATTR_DESCR_MASK) != L3_PAGE) {
|
if ((l3 & AARCH64_ATTR_DESCR_MASK) != AARCH64_L3_PAGE) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pde not valid");
|
_kvm_err(kd, kd->program,
|
||||||
|
"_aarch64_minidump_vatop: pde not valid");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
a = l3 & ~ATTR_MASK;
|
a = l3 & ~AARCH64_ATTR_MASK;
|
||||||
ofs = hpt_find(kd, a);
|
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||||
if (ofs == -1) {
|
if (ofs == -1) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: "
|
_kvm_err(kd, kd->program, "_aarch64_minidump_vatop: "
|
||||||
"physical address 0x%lx not in minidump", a);
|
"physical address 0x%jx not in minidump",
|
||||||
|
(uintmax_t)a);
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
*pa = ofs + offset;
|
*pa = ofs + offset;
|
||||||
return (PAGE_SIZE - offset);
|
return (AARCH64_PAGE_SIZE - offset);
|
||||||
} else {
|
} else {
|
||||||
_kvm_err(kd, kd->program,
|
_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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
_aarch64_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (ISALIVE(kd)) {
|
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 (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/param.h>
|
||||||
#include <sys/user.h>
|
#include <sys/endian.h>
|
||||||
#include <sys/proc.h>
|
#include <stdint.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/fnv_hash.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <nlist.h>
|
|
||||||
#include <kvm.h>
|
#include <kvm.h>
|
||||||
|
|
||||||
#include <vm/vm.h>
|
#include "../../sys/amd64/include/minidump.h"
|
||||||
#include <vm/vm_param.h>
|
|
||||||
|
|
||||||
#include <machine/elf.h>
|
|
||||||
#include <machine/cpufunc.h>
|
|
||||||
#include <machine/minidump.h>
|
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#include "kvm_private.h"
|
#include "kvm_private.h"
|
||||||
|
#include "kvm_amd64.h"
|
||||||
|
|
||||||
struct hpte {
|
#define amd64_round_page(x) roundup2((kvaddr_t)(x), AMD64_PAGE_SIZE)
|
||||||
struct hpte *next;
|
|
||||||
vm_paddr_t pa;
|
|
||||||
int64_t off;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define HPT_SIZE 1024
|
|
||||||
|
|
||||||
/* minidump must be the first item! */
|
|
||||||
struct vmstate {
|
struct vmstate {
|
||||||
int minidump; /* 1 = minidump mode */
|
|
||||||
struct minidumphdr hdr;
|
struct minidumphdr hdr;
|
||||||
void *hpt_head[HPT_SIZE];
|
struct hpt hpt;
|
||||||
uint64_t *bitmap;
|
amd64_pte_t *page_map;
|
||||||
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
|
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++) {
|
return (_kvm_probe_elf_kernel(kd, ELFCLASS64, EM_X86_64) &&
|
||||||
bits = base[idx];
|
_kvm_is_minidump(kd));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
_kvm_minidump_freevtop(kvm_t *kd)
|
_amd64_minidump_freevtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vm = kd->vmst;
|
struct vmstate *vm = kd->vmst;
|
||||||
|
|
||||||
if (vm->bitmap)
|
_kvm_hpt_free(&vm->hpt);
|
||||||
free(vm->bitmap);
|
|
||||||
if (vm->page_map)
|
if (vm->page_map)
|
||||||
free(vm->page_map);
|
free(vm->page_map);
|
||||||
free(vm);
|
free(vm);
|
||||||
kd->vmst = NULL;
|
kd->vmst = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_minidump_initvtop(kvm_t *kd)
|
_amd64_minidump_initvtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vmst;
|
struct vmstate *vmst;
|
||||||
|
uint64_t *bitmap;
|
||||||
off_t off;
|
off_t off;
|
||||||
|
|
||||||
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
||||||
|
|
@ -145,7 +86,6 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
kd->vmst = vmst;
|
kd->vmst = vmst;
|
||||||
vmst->minidump = 1;
|
|
||||||
if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
|
if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
|
||||||
sizeof(vmst->hdr)) {
|
sizeof(vmst->hdr)) {
|
||||||
_kvm_err(kd, kd->program, "cannot read dump header");
|
_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
|
* NB: amd64 minidump header is binary compatible between version 1
|
||||||
* and version 2; this may not be the case for the future versions.
|
* 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) {
|
if (vmst->hdr.version != MINIDUMP_VERSION && vmst->hdr.version != 1) {
|
||||||
_kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d",
|
_kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d",
|
||||||
MINIDUMP_VERSION, vmst->hdr.version);
|
MINIDUMP_VERSION, vmst->hdr.version);
|
||||||
return (-1);
|
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 */
|
/* 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);
|
bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
|
||||||
if (vmst->bitmap == NULL) {
|
if (bitmap == NULL) {
|
||||||
_kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize);
|
_kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (pread(kd->pmfd, vmst->bitmap, vmst->hdr.bitmapsize, off) !=
|
if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
|
||||||
vmst->hdr.bitmapsize) {
|
(ssize_t)vmst->hdr.bitmapsize) {
|
||||||
_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize);
|
_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
off += round_page(vmst->hdr.bitmapsize);
|
off += amd64_round_page(vmst->hdr.bitmapsize);
|
||||||
|
|
||||||
vmst->page_map = _kvm_malloc(kd, vmst->hdr.pmapsize);
|
vmst->page_map = _kvm_malloc(kd, vmst->hdr.pmapsize);
|
||||||
if (vmst->page_map == NULL) {
|
if (vmst->page_map == NULL) {
|
||||||
_kvm_err(kd, kd->program, "cannot allocate %d bytes for page_map", vmst->hdr.pmapsize);
|
_kvm_err(kd, kd->program, "cannot allocate %d bytes for page_map", vmst->hdr.pmapsize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (pread(kd->pmfd, vmst->page_map, vmst->hdr.pmapsize, off) !=
|
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);
|
_kvm_err(kd, kd->program, "cannot read %d bytes for page_map", vmst->hdr.pmapsize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
off += vmst->hdr.pmapsize;
|
off += vmst->hdr.pmapsize;
|
||||||
|
|
||||||
/* build physical address hash table for sparse pages */
|
/* 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);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct vmstate *vm;
|
||||||
u_long offset;
|
amd64_physaddr_t offset;
|
||||||
pt_entry_t pte;
|
amd64_pte_t pte;
|
||||||
u_long pteindex;
|
kvaddr_t pteindex;
|
||||||
u_long a;
|
amd64_physaddr_t a;
|
||||||
off_t ofs;
|
off_t ofs;
|
||||||
|
|
||||||
vm = kd->vmst;
|
vm = kd->vmst;
|
||||||
offset = va & (PAGE_SIZE - 1);
|
offset = va & AMD64_PAGE_MASK;
|
||||||
|
|
||||||
if (va >= vm->hdr.kernbase) {
|
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))
|
if (pteindex >= vm->hdr.pmapsize / sizeof(*vm->page_map))
|
||||||
goto invalid;
|
goto invalid;
|
||||||
pte = vm->page_map[pteindex];
|
pte = le64toh(vm->page_map[pteindex]);
|
||||||
if (((u_long)pte & PG_V) == 0) {
|
if ((pte & AMD64_PG_V) == 0) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
|
_kvm_err(kd, kd->program,
|
||||||
|
"_amd64_minidump_vatop_v1: pte not valid");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
a = pte & PG_FRAME;
|
a = pte & AMD64_PG_FRAME;
|
||||||
ofs = hpt_find(kd, a);
|
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||||
if (ofs == -1) {
|
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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
*pa = ofs + offset;
|
*pa = ofs + offset;
|
||||||
return (PAGE_SIZE - offset);
|
return (AMD64_PAGE_SIZE - offset);
|
||||||
} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
|
} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
|
||||||
a = (va - vm->hdr.dmapbase) & ~PAGE_MASK;
|
a = (va - vm->hdr.dmapbase) & ~AMD64_PAGE_MASK;
|
||||||
ofs = hpt_find(kd, a);
|
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||||
if (ofs == -1) {
|
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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
*pa = ofs + offset;
|
*pa = ofs + offset;
|
||||||
return (PAGE_SIZE - offset);
|
return (AMD64_PAGE_SIZE - offset);
|
||||||
} else {
|
} 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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct vmstate *vm;
|
||||||
u_long offset;
|
amd64_physaddr_t offset;
|
||||||
pd_entry_t pde;
|
amd64_pde_t pde;
|
||||||
pd_entry_t pte;
|
amd64_pte_t pte;
|
||||||
u_long pteindex;
|
kvaddr_t pteindex;
|
||||||
u_long pdeindex;
|
kvaddr_t pdeindex;
|
||||||
u_long a;
|
amd64_physaddr_t a;
|
||||||
off_t ofs;
|
off_t ofs;
|
||||||
|
|
||||||
vm = kd->vmst;
|
vm = kd->vmst;
|
||||||
offset = va & PAGE_MASK;
|
offset = va & AMD64_PAGE_MASK;
|
||||||
|
|
||||||
if (va >= vm->hdr.kernbase) {
|
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))
|
if (pdeindex >= vm->hdr.pmapsize / sizeof(*vm->page_map))
|
||||||
goto invalid;
|
goto invalid;
|
||||||
pde = vm->page_map[pdeindex];
|
pde = le64toh(vm->page_map[pdeindex]);
|
||||||
if (((u_long)pde & PG_V) == 0) {
|
if ((pde & AMD64_PG_V) == 0) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pde not valid");
|
_kvm_err(kd, kd->program,
|
||||||
|
"_amd64_minidump_vatop: pde not valid");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
if ((pde & PG_PS) == 0) {
|
if ((pde & AMD64_PG_PS) == 0) {
|
||||||
a = pde & PG_FRAME;
|
a = pde & AMD64_PG_FRAME;
|
||||||
ofs = hpt_find(kd, a);
|
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||||
if (ofs == -1) {
|
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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
if (pread(kd->pmfd, &pt, PAGE_SIZE, ofs) != PAGE_SIZE) {
|
/* TODO: Just read the single PTE */
|
||||||
_kvm_err(kd, kd->program, "cannot read %d bytes for pt", PAGE_SIZE);
|
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);
|
return (-1);
|
||||||
}
|
}
|
||||||
pteindex = (va >> PAGE_SHIFT) & ((1ul << NPTEPGSHIFT) - 1);
|
pteindex = (va >> AMD64_PAGE_SHIFT) &
|
||||||
pte = pt[pteindex];
|
(AMD64_NPTEPG - 1);
|
||||||
if (((u_long)pte & PG_V) == 0) {
|
pte = le64toh(pt[pteindex]);
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
|
if ((pte & AMD64_PG_V) == 0) {
|
||||||
|
_kvm_err(kd, kd->program,
|
||||||
|
"_amd64_minidump_vatop: pte not valid");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
a = pte & PG_FRAME;
|
a = pte & AMD64_PG_FRAME;
|
||||||
} else {
|
} else {
|
||||||
a = pde & PG_PS_FRAME;
|
a = pde & AMD64_PG_PS_FRAME;
|
||||||
a += (va & PDRMASK) ^ offset;
|
a += (va & AMD64_PDRMASK) ^ offset;
|
||||||
}
|
}
|
||||||
ofs = hpt_find(kd, a);
|
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||||
if (ofs == -1) {
|
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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
*pa = ofs + offset;
|
*pa = ofs + offset;
|
||||||
return (PAGE_SIZE - offset);
|
return (AMD64_PAGE_SIZE - offset);
|
||||||
} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
|
} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
|
||||||
a = (va - vm->hdr.dmapbase) & ~PAGE_MASK;
|
a = (va - vm->hdr.dmapbase) & ~AMD64_PAGE_MASK;
|
||||||
ofs = hpt_find(kd, a);
|
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||||
if (ofs == -1) {
|
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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
*pa = ofs + offset;
|
*pa = ofs + offset;
|
||||||
return (PAGE_SIZE - offset);
|
return (AMD64_PAGE_SIZE - offset);
|
||||||
} else {
|
} 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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
_amd64_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (ISALIVE(kd)) {
|
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);
|
return (0);
|
||||||
}
|
}
|
||||||
if (((struct vmstate *)kd->vmst)->hdr.version == 1)
|
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
|
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.
|
* ARM machine dependent routines for kvm and minidumps.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <sys/endian.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#ifndef CROSS_LIBKVM
|
#include <kvm.h>
|
||||||
#include <sys/user.h>
|
#include <limits.h>
|
||||||
#endif
|
#include <stdint.h>
|
||||||
#include <sys/proc.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/fnv_hash.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.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"
|
#include "../../sys/arm/include/minidump.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include "kvm_private.h"
|
#include "kvm_private.h"
|
||||||
|
#include "kvm_arm.h"
|
||||||
|
|
||||||
struct hpte {
|
#define arm_round_page(x) roundup2((kvaddr_t)(x), ARM_PAGE_SIZE)
|
||||||
struct hpte *next;
|
|
||||||
uint64_t pa;
|
|
||||||
int64_t off;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define HPT_SIZE 1024
|
|
||||||
|
|
||||||
/* minidump must be the first field */
|
|
||||||
struct vmstate {
|
struct vmstate {
|
||||||
int minidump; /* 1 = minidump mode */
|
|
||||||
struct minidumphdr hdr;
|
struct minidumphdr hdr;
|
||||||
void *hpt_head[HPT_SIZE];
|
struct hpt hpt;
|
||||||
uint32_t *bitmap;
|
|
||||||
void *ptemap;
|
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
|
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++) {
|
return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_ARM) &&
|
||||||
bits = base[idx];
|
_kvm_is_minidump(kd));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
_kvm_minidump_freevtop(kvm_t *kd)
|
_arm_minidump_freevtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vm = kd->vmst;
|
struct vmstate *vm = kd->vmst;
|
||||||
|
|
||||||
if (vm->bitmap)
|
_kvm_hpt_free(&vm->hpt);
|
||||||
free(vm->bitmap);
|
|
||||||
if (vm->ptemap)
|
if (vm->ptemap)
|
||||||
free(vm->ptemap);
|
free(vm->ptemap);
|
||||||
free(vm);
|
free(vm);
|
||||||
kd->vmst = NULL;
|
kd->vmst = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_minidump_initvtop(kvm_t *kd)
|
_arm_minidump_initvtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vmst;
|
struct vmstate *vmst;
|
||||||
|
uint32_t *bitmap;
|
||||||
off_t off;
|
off_t off;
|
||||||
|
|
||||||
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
||||||
|
|
@ -156,7 +90,6 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||||
}
|
}
|
||||||
|
|
||||||
kd->vmst = vmst;
|
kd->vmst = vmst;
|
||||||
vmst->minidump = 1;
|
|
||||||
|
|
||||||
if (pread(kd->pmfd, &vmst->hdr,
|
if (pread(kd->pmfd, &vmst->hdr,
|
||||||
sizeof(vmst->hdr), 0) != sizeof(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");
|
_kvm_err(kd, kd->program, "not a minidump for this platform");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version);
|
||||||
if (vmst->hdr.version != MINIDUMP_VERSION) {
|
if (vmst->hdr.version != MINIDUMP_VERSION) {
|
||||||
_kvm_err(kd, kd->program, "wrong minidump version. "
|
_kvm_err(kd, kd->program, "wrong minidump version. "
|
||||||
"Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
|
"Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
|
||||||
return (-1);
|
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 */
|
/* 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);
|
bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
|
||||||
if (vmst->bitmap == NULL) {
|
if (bitmap == NULL) {
|
||||||
_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
|
_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
|
||||||
"bitmap", vmst->hdr.bitmapsize);
|
"bitmap", vmst->hdr.bitmapsize);
|
||||||
return (-1);
|
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) {
|
(ssize_t)vmst->hdr.bitmapsize) {
|
||||||
_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
|
_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
|
||||||
vmst->hdr.bitmapsize);
|
vmst->hdr.bitmapsize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
off += round_page(vmst->hdr.bitmapsize);
|
off += arm_round_page(vmst->hdr.bitmapsize);
|
||||||
|
|
||||||
vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
|
vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
|
||||||
if (vmst->ptemap == NULL) {
|
if (vmst->ptemap == NULL) {
|
||||||
_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
|
_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
|
||||||
"ptemap", vmst->hdr.ptesize);
|
"ptemap", vmst->hdr.ptesize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,28 +144,32 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||||
(ssize_t)vmst->hdr.ptesize) {
|
(ssize_t)vmst->hdr.ptesize) {
|
||||||
_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
|
_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
|
||||||
vmst->hdr.ptesize);
|
vmst->hdr.ptesize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
off += vmst->hdr.ptesize;
|
off += vmst->hdr.ptesize;
|
||||||
|
|
||||||
/* Build physical address hash table for sparse pages */
|
/* 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);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
_arm_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||||
{
|
{
|
||||||
struct vmstate *vm;
|
struct vmstate *vm;
|
||||||
pt_entry_t pte;
|
arm_pt_entry_t pte;
|
||||||
u_long offset, pteindex, a;
|
arm_physaddr_t offset, a;
|
||||||
|
kvaddr_t pteindex;
|
||||||
off_t ofs;
|
off_t ofs;
|
||||||
uint32_t *ptemap;
|
arm_pt_entry_t *ptemap;
|
||||||
|
|
||||||
if (ISALIVE(kd)) {
|
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);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,36 +177,48 @@ _kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
||||||
ptemap = vm->ptemap;
|
ptemap = vm->ptemap;
|
||||||
|
|
||||||
if (va >= vm->hdr.kernbase) {
|
if (va >= vm->hdr.kernbase) {
|
||||||
pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
|
pteindex = (va - vm->hdr.kernbase) >> ARM_PAGE_SHIFT;
|
||||||
pte = ptemap[pteindex];
|
pte = _kvm32toh(kd, ptemap[pteindex]);
|
||||||
if (!pte) {
|
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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
if ((pte & L2_TYPE_MASK) == L2_TYPE_L) {
|
if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_L) {
|
||||||
offset = va & L2_L_OFFSET;
|
offset = va & ARM_L2_L_OFFSET;
|
||||||
a = pte & L2_L_FRAME;
|
a = pte & ARM_L2_L_FRAME;
|
||||||
} else if ((pte & L2_TYPE_MASK) == L2_TYPE_S) {
|
} else if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_S) {
|
||||||
offset = va & L2_S_OFFSET;
|
offset = va & ARM_L2_S_OFFSET;
|
||||||
a = pte & L2_S_FRAME;
|
a = pte & ARM_L2_S_FRAME;
|
||||||
} else
|
} else
|
||||||
goto invalid;
|
goto invalid;
|
||||||
|
|
||||||
ofs = hpt_find(kd, a);
|
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||||
if (ofs == -1) {
|
if (ofs == -1) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: physical "
|
_kvm_err(kd, kd->program, "_arm_minidump_kvatop: "
|
||||||
"address 0x%lx not in minidump", a);
|
"physical address 0x%jx not in minidump",
|
||||||
|
(uintmax_t)a);
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
*pa = ofs + offset;
|
*pa = ofs + offset;
|
||||||
return (PAGE_SIZE - offset);
|
return (ARM_PAGE_SIZE - offset);
|
||||||
|
|
||||||
} else
|
} else
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx "
|
_kvm_err(kd, kd->program, "_arm_minidump_kvatop: virtual "
|
||||||
"not minidumped", va);
|
"address 0x%jx not minidumped", (uintmax_t)va);
|
||||||
|
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||||
return (0);
|
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$");
|
__FBSDID("$FreeBSD$");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AMD64 machine dependent routines for kvm and minidumps.
|
* i386 machine dependent routines for kvm and minidumps.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/user.h>
|
#include <sys/endian.h>
|
||||||
#include <sys/proc.h>
|
#include <stdint.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/fnv_hash.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <nlist.h>
|
|
||||||
#include <kvm.h>
|
#include <kvm.h>
|
||||||
|
|
||||||
#include <vm/vm.h>
|
#include "../../sys/i386/include/minidump.h"
|
||||||
#include <vm/vm_param.h>
|
|
||||||
|
|
||||||
#include <machine/elf.h>
|
|
||||||
#include <machine/cpufunc.h>
|
|
||||||
#include <machine/minidump.h>
|
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#include "kvm_private.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 {
|
struct vmstate {
|
||||||
int minidump; /* 1 = minidump mode */
|
|
||||||
struct minidumphdr hdr;
|
struct minidumphdr hdr;
|
||||||
void *hpt_head[HPT_SIZE];
|
struct hpt hpt;
|
||||||
uint32_t *bitmap;
|
|
||||||
void *ptemap;
|
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
|
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++) {
|
return (_kvm_probe_elf_kernel(kd, ELFCLASS32, EM_386) &&
|
||||||
bits = base[idx];
|
_kvm_is_minidump(kd));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
_kvm_minidump_freevtop(kvm_t *kd)
|
_i386_minidump_freevtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vm = kd->vmst;
|
struct vmstate *vm = kd->vmst;
|
||||||
|
|
||||||
if (vm->bitmap)
|
_kvm_hpt_free(&vm->hpt);
|
||||||
free(vm->bitmap);
|
|
||||||
if (vm->ptemap)
|
if (vm->ptemap)
|
||||||
free(vm->ptemap);
|
free(vm->ptemap);
|
||||||
free(vm);
|
free(vm);
|
||||||
kd->vmst = NULL;
|
kd->vmst = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_minidump_initvtop(kvm_t *kd)
|
_i386_minidump_initvtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vmst;
|
struct vmstate *vmst;
|
||||||
|
uint32_t *bitmap;
|
||||||
off_t off;
|
off_t off;
|
||||||
|
|
||||||
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
||||||
|
|
@ -147,7 +86,6 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
kd->vmst = vmst;
|
kd->vmst = vmst;
|
||||||
vmst->minidump = 1;
|
|
||||||
if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
|
if (pread(kd->pmfd, &vmst->hdr, sizeof(vmst->hdr), 0) !=
|
||||||
sizeof(vmst->hdr)) {
|
sizeof(vmst->hdr)) {
|
||||||
_kvm_err(kd, kd->program, "cannot read dump header");
|
_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");
|
_kvm_err(kd, kd->program, "not a minidump for this platform");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
vmst->hdr.version = le32toh(vmst->hdr.version);
|
||||||
if (vmst->hdr.version != MINIDUMP_VERSION) {
|
if (vmst->hdr.version != MINIDUMP_VERSION) {
|
||||||
_kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d",
|
_kvm_err(kd, kd->program, "wrong minidump version. expected %d got %d",
|
||||||
MINIDUMP_VERSION, vmst->hdr.version);
|
MINIDUMP_VERSION, vmst->hdr.version);
|
||||||
return (-1);
|
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 */
|
/* 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);
|
bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
|
||||||
if (vmst->bitmap == NULL) {
|
if (bitmap == NULL) {
|
||||||
_kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize);
|
_kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize);
|
||||||
return (-1);
|
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) {
|
(ssize_t)vmst->hdr.bitmapsize) {
|
||||||
_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize);
|
_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
off += round_page(vmst->hdr.bitmapsize);
|
off += i386_round_page(vmst->hdr.bitmapsize);
|
||||||
|
|
||||||
vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
|
vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
|
||||||
if (vmst->ptemap == NULL) {
|
if (vmst->ptemap == NULL) {
|
||||||
_kvm_err(kd, kd->program, "cannot allocate %d bytes for ptemap", vmst->hdr.ptesize);
|
_kvm_err(kd, kd->program, "cannot allocate %d bytes for ptemap", vmst->hdr.ptesize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
|
if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
|
||||||
(ssize_t)vmst->hdr.ptesize) {
|
(ssize_t)vmst->hdr.ptesize) {
|
||||||
_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize);
|
_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
off += vmst->hdr.ptesize;
|
off += vmst->hdr.ptesize;
|
||||||
|
|
||||||
/* build physical address hash table for sparse pages */
|
/* 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);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct vmstate *vm;
|
||||||
uint64_t offset;
|
i386_physaddr_pae_t offset;
|
||||||
uint64_t pte;
|
i386_pte_pae_t pte;
|
||||||
u_long pteindex;
|
kvaddr_t pteindex;
|
||||||
uint64_t a;
|
i386_physaddr_pae_t a;
|
||||||
off_t ofs;
|
off_t ofs;
|
||||||
uint64_t *ptemap;
|
i386_pte_pae_t *ptemap;
|
||||||
|
|
||||||
vm = kd->vmst;
|
vm = kd->vmst;
|
||||||
ptemap = vm->ptemap;
|
ptemap = vm->ptemap;
|
||||||
offset = va & (PAGE_SIZE - 1);
|
offset = va & I386_PAGE_MASK;
|
||||||
|
|
||||||
if (va >= vm->hdr.kernbase) {
|
if (va >= vm->hdr.kernbase) {
|
||||||
pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
|
pteindex = (va - vm->hdr.kernbase) >> I386_PAGE_SHIFT;
|
||||||
pte = ptemap[pteindex];
|
pte = le64toh(ptemap[pteindex]);
|
||||||
if ((pte & PG_V) == 0) {
|
if ((pte & I386_PG_V) == 0) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
|
_kvm_err(kd, kd->program,
|
||||||
|
"_i386_minidump_vatop_pae: pte not valid");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
a = pte & PG_FRAME_PAE;
|
a = pte & I386_PG_FRAME_PAE;
|
||||||
ofs = hpt_find(kd, a);
|
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||||
if (ofs == -1) {
|
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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
*pa = ofs + offset;
|
*pa = ofs + offset;
|
||||||
return (PAGE_SIZE - offset);
|
return (I386_PAGE_SIZE - offset);
|
||||||
} else {
|
} 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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct vmstate *vm;
|
||||||
u_long offset;
|
i386_physaddr_t offset;
|
||||||
pt_entry_t pte;
|
i386_pte_t pte;
|
||||||
u_long pteindex;
|
kvaddr_t pteindex;
|
||||||
u_long a;
|
i386_physaddr_t a;
|
||||||
off_t ofs;
|
off_t ofs;
|
||||||
uint32_t *ptemap;
|
i386_pte_t *ptemap;
|
||||||
|
|
||||||
vm = kd->vmst;
|
vm = kd->vmst;
|
||||||
ptemap = vm->ptemap;
|
ptemap = vm->ptemap;
|
||||||
offset = va & (PAGE_SIZE - 1);
|
offset = va & I386_PAGE_MASK;
|
||||||
|
|
||||||
if (va >= vm->hdr.kernbase) {
|
if (va >= vm->hdr.kernbase) {
|
||||||
pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
|
pteindex = (va - vm->hdr.kernbase) >> I386_PAGE_SHIFT;
|
||||||
pte = ptemap[pteindex];
|
pte = le32toh(ptemap[pteindex]);
|
||||||
if ((pte & PG_V) == 0) {
|
if ((pte & I386_PG_V) == 0) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: pte not valid");
|
_kvm_err(kd, kd->program,
|
||||||
|
"_i386_minidump_vatop: pte not valid");
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
a = pte & PG_FRAME;
|
a = pte & I386_PG_FRAME;
|
||||||
ofs = hpt_find(kd, a);
|
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||||
if (ofs == -1) {
|
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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
*pa = ofs + offset;
|
*pa = ofs + offset;
|
||||||
return (PAGE_SIZE - offset);
|
return (I386_PAGE_SIZE - offset);
|
||||||
} else {
|
} 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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
_i386_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (ISALIVE(kd)) {
|
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);
|
return (0);
|
||||||
}
|
}
|
||||||
if (kd->vmst->hdr.paemode)
|
if (kd->vmst->hdr.paemode)
|
||||||
return (_kvm_minidump_vatop_pae(kd, va, pa));
|
return (_i386_minidump_vatop_pae(kd, va, pa));
|
||||||
else
|
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/param.h>
|
||||||
#include <sys/user.h>
|
#include <kvm.h>
|
||||||
#include <sys/proc.h>
|
#include <limits.h>
|
||||||
#include <sys/stat.h>
|
#include <stdint.h>
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/fnv_hash.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <nlist.h>
|
|
||||||
#include <kvm.h>
|
|
||||||
|
|
||||||
#include <vm/vm.h>
|
#include "../../sys/mips/include/cpuregs.h"
|
||||||
#include <vm/vm_param.h>
|
#include "../../sys/mips/include/minidump.h"
|
||||||
|
|
||||||
#include <machine/elf.h>
|
|
||||||
#include <machine/cpufunc.h>
|
|
||||||
#include <machine/minidump.h>
|
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include "kvm_private.h"
|
#include "kvm_private.h"
|
||||||
|
#include "kvm_mips.h"
|
||||||
|
|
||||||
struct hpte {
|
#define mips_round_page(x) roundup2((kvaddr_t)(x), MIPS_PAGE_SIZE)
|
||||||
struct hpte *next;
|
|
||||||
uint64_t pa;
|
|
||||||
int64_t off;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define HPT_SIZE 1024
|
|
||||||
|
|
||||||
/* minidump must be the first field */
|
|
||||||
struct vmstate {
|
struct vmstate {
|
||||||
int minidump; /* 1 = minidump mode */
|
|
||||||
struct minidumphdr hdr;
|
struct minidumphdr hdr;
|
||||||
void *hpt_head[HPT_SIZE];
|
struct hpt hpt;
|
||||||
uint32_t *bitmap;
|
|
||||||
void *ptemap;
|
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
|
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++) {
|
if (kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS32 &&
|
||||||
bits = base[idx];
|
kd->nlehdr.e_ident[EI_CLASS] != ELFCLASS64)
|
||||||
while (bits) {
|
return (0);
|
||||||
bit = ffs(bits) - 1;
|
if (kd->nlehdr.e_machine != EM_MIPS)
|
||||||
bits &= ~(1ul << bit);
|
return (0);
|
||||||
pa = (idx * sizeof(*base) * NBBY + bit) * PAGE_SIZE;
|
return (_kvm_is_minidump(kd));
|
||||||
hpt_insert(kd, pa, off);
|
|
||||||
off += PAGE_SIZE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (off);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
_kvm_minidump_freevtop(kvm_t *kd)
|
_mips_minidump_freevtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vm = kd->vmst;
|
struct vmstate *vm = kd->vmst;
|
||||||
|
|
||||||
if (vm->bitmap)
|
_kvm_hpt_free(&vm->hpt);
|
||||||
free(vm->bitmap);
|
|
||||||
if (vm->ptemap)
|
if (vm->ptemap)
|
||||||
free(vm->ptemap);
|
free(vm->ptemap);
|
||||||
free(vm);
|
free(vm);
|
||||||
kd->vmst = NULL;
|
kd->vmst = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_minidump_initvtop(kvm_t *kd)
|
_mips_minidump_initvtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vmst;
|
struct vmstate *vmst;
|
||||||
|
uint32_t *bitmap;
|
||||||
off_t off;
|
off_t off;
|
||||||
|
|
||||||
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
vmst = _kvm_malloc(kd, sizeof(*vmst));
|
||||||
|
|
@ -150,9 +95,13 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||||
}
|
}
|
||||||
|
|
||||||
kd->vmst = vmst;
|
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,
|
if (pread(kd->pmfd, &vmst->hdr,
|
||||||
sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
|
sizeof(vmst->hdr), 0) != sizeof(vmst->hdr)) {
|
||||||
_kvm_err(kd, kd->program, "cannot read dump header");
|
_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");
|
_kvm_err(kd, kd->program, "not a minidump for this platform");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
vmst->hdr.version = _kvm32toh(kd, vmst->hdr.version);
|
||||||
if (vmst->hdr.version != MINIDUMP_VERSION) {
|
if (vmst->hdr.version != MINIDUMP_VERSION) {
|
||||||
_kvm_err(kd, kd->program, "wrong minidump version. "
|
_kvm_err(kd, kd->program, "wrong minidump version. "
|
||||||
"Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
|
"Expected %d got %d", MINIDUMP_VERSION, vmst->hdr.version);
|
||||||
return (-1);
|
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 */
|
/* 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);
|
bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
|
||||||
if (vmst->bitmap == NULL) {
|
if (bitmap == NULL) {
|
||||||
_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
|
_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
|
||||||
"bitmap", vmst->hdr.bitmapsize);
|
"bitmap", vmst->hdr.bitmapsize);
|
||||||
return (-1);
|
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) {
|
(ssize_t)vmst->hdr.bitmapsize) {
|
||||||
_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
|
_kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
|
||||||
vmst->hdr.bitmapsize);
|
vmst->hdr.bitmapsize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
off += round_page(vmst->hdr.bitmapsize);
|
off += mips_round_page(vmst->hdr.bitmapsize);
|
||||||
|
|
||||||
vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
|
vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
|
||||||
if (vmst->ptemap == NULL) {
|
if (vmst->ptemap == NULL) {
|
||||||
_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
|
_kvm_err(kd, kd->program, "cannot allocate %d bytes for "
|
||||||
"ptemap", vmst->hdr.ptesize);
|
"ptemap", vmst->hdr.ptesize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,75 +157,139 @@ _kvm_minidump_initvtop(kvm_t *kd)
|
||||||
(ssize_t)vmst->hdr.ptesize) {
|
(ssize_t)vmst->hdr.ptesize) {
|
||||||
_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
|
_kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
|
||||||
vmst->hdr.ptesize);
|
vmst->hdr.ptesize);
|
||||||
|
free(bitmap);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
off += vmst->hdr.ptesize;
|
off += vmst->hdr.ptesize;
|
||||||
|
|
||||||
/* Build physical address hash table for sparse pages */
|
/* 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);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_minidump_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
_mips_minidump_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||||
{
|
{
|
||||||
struct vmstate *vm;
|
struct vmstate *vm;
|
||||||
pt_entry_t pte;
|
uint64_t pte;
|
||||||
u_long offset, pteindex, a;
|
mips_physaddr_t offset, a;
|
||||||
|
kvaddr_t pteindex;
|
||||||
off_t ofs;
|
off_t ofs;
|
||||||
pt_entry_t *ptemap;
|
uint32_t *ptemap32;
|
||||||
|
uint64_t *ptemap64;
|
||||||
|
|
||||||
if (ISALIVE(kd)) {
|
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);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = va & PAGE_MASK;
|
offset = va & MIPS_PAGE_MASK;
|
||||||
/* Operate with page-aligned address */
|
/* Operate with page-aligned address */
|
||||||
va &= ~PAGE_MASK;
|
va &= ~MIPS_PAGE_MASK;
|
||||||
|
|
||||||
vm = kd->vmst;
|
vm = kd->vmst;
|
||||||
ptemap = vm->ptemap;
|
ptemap32 = vm->ptemap;
|
||||||
|
ptemap64 = vm->ptemap;
|
||||||
|
|
||||||
#if defined(__mips_n64)
|
if (kd->nlehdr.e_ident[EI_CLASS] == ELFCLASS64) {
|
||||||
if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END)
|
if (va >= MIPS_XKPHYS_START && va < MIPS_XKPHYS_END) {
|
||||||
a = (MIPS_XKPHYS_TO_PHYS(va));
|
a = va & MIPS_XKPHYS_PHYS_MASK;
|
||||||
else
|
goto found;
|
||||||
#endif
|
}
|
||||||
if (va >= (u_long)MIPS_KSEG0_START && va < (u_long)MIPS_KSEG0_END)
|
if (va >= MIPS64_KSEG0_START && va < MIPS64_KSEG0_END) {
|
||||||
a = (MIPS_KSEG0_TO_PHYS(va));
|
a = va & MIPS_KSEG0_PHYS_MASK;
|
||||||
else if (va >= (u_long)MIPS_KSEG1_START && va < (u_long)MIPS_KSEG1_END)
|
goto found;
|
||||||
a = (MIPS_KSEG1_TO_PHYS(va));
|
}
|
||||||
else if (va >= vm->hdr.kernbase) {
|
if (va >= MIPS64_KSEG1_START && va < MIPS64_KSEG1_END) {
|
||||||
pteindex = (va - vm->hdr.kernbase) >> PAGE_SHIFT;
|
a = va & MIPS_KSEG0_PHYS_MASK;
|
||||||
pte = ptemap[pteindex];
|
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) {
|
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;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
a = TLBLO_PTE_TO_PA(pte);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: virtual address 0x%lx "
|
_kvm_err(kd, kd->program, "_mips_minidump_kvatop: virtual "
|
||||||
"not minidumped", va);
|
"address 0x%jx not minidumped", (uintmax_t)va);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ofs = hpt_find(kd, a);
|
found:
|
||||||
|
ofs = _kvm_hpt_find(&vm->hpt, a);
|
||||||
if (ofs == -1) {
|
if (ofs == -1) {
|
||||||
_kvm_err(kd, kd->program, "_kvm_vatop: physical "
|
_kvm_err(kd, kd->program, "_mips_minidump_kvatop: physical "
|
||||||
"address 0x%lx not in minidump", a);
|
"address 0x%jx not in minidump", (uintmax_t)a);
|
||||||
goto invalid;
|
goto invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
*pa = ofs + offset;
|
*pa = ofs + offset;
|
||||||
return (PAGE_SIZE - offset);
|
return (MIPS_PAGE_SIZE - offset);
|
||||||
|
|
||||||
|
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "invalid address (0x%lx)", va);
|
_kvm_err(kd, 0, "invalid address (0x%jx)", (uintmax_t)va);
|
||||||
return (0);
|
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
|
.\" @(#)kvm_nlist.3 8.1 (Berkeley) 6/4/93
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd June 4, 1993
|
.Dd November 27, 2015
|
||||||
.Dt KVM_NLIST 3
|
.Dt KVM_NLIST 3
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
.Nm kvm_nlist
|
.Nm kvm_nlist ,
|
||||||
|
.Nm kvm_nlist2
|
||||||
.Nd retrieve symbol table names from a kernel image
|
.Nd retrieve symbol table names from a kernel image
|
||||||
.Sh LIBRARY
|
.Sh LIBRARY
|
||||||
.Lb libkvm
|
.Lb libkvm
|
||||||
|
|
@ -45,31 +46,62 @@
|
||||||
.In nlist.h
|
.In nlist.h
|
||||||
.Ft int
|
.Ft int
|
||||||
.Fn kvm_nlist "kvm_t *kd" "struct nlist *nl"
|
.Fn kvm_nlist "kvm_t *kd" "struct nlist *nl"
|
||||||
|
.Ft int
|
||||||
|
.Fn kvm_nlist2 "kvm_t *kd" "struct kvm_nlist *nl"
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
The
|
The
|
||||||
.Fn kvm_nlist
|
.Fn kvm_nlist
|
||||||
function retrieves the symbol table entries indicated by the name list argument
|
function retrieves the symbol table entries indicated by the name list argument
|
||||||
.Fa \&nl .
|
.Fa \&nl .
|
||||||
This argument points to an array of nlist structures, terminated by
|
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
|
.Dv NULL
|
||||||
(see
|
(see
|
||||||
.Xr nlist 3 ) .
|
.Xr nlist 3 ) .
|
||||||
Each symbol is looked up using the n_name field, and if found, the
|
Each symbol is looked up using the
|
||||||
corresponding n_type and n_value fields are filled in.
|
.Fa n_name
|
||||||
|
field, and if found, the
|
||||||
|
corresponding
|
||||||
|
.Fa n_type
|
||||||
|
and
|
||||||
|
.Fa n_value
|
||||||
|
fields are filled in.
|
||||||
These fields are set
|
These fields are set
|
||||||
to 0 if the symbol is not found.
|
to 0 if the symbol is not found.
|
||||||
.Pp
|
.Pp
|
||||||
The
|
The
|
||||||
.Xr kldsym 2
|
.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
|
This is a less than perfect
|
||||||
emulation of the nlist values but has the advantage of being aware of kernel
|
emulation of the nlist values but has the advantage of being aware of kernel
|
||||||
modules and is reasonably fast.
|
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
|
.Sh RETURN VALUES
|
||||||
The
|
The
|
||||||
.Fn kvm_nlist
|
.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.
|
If the kernel symbol table was unreadable, -1 is returned.
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr kldsym 2 ,
|
.Xr kldsym 2 ,
|
||||||
|
|
@ -79,6 +111,7 @@ If the kernel symbol table was unreadable, -1 is returned.
|
||||||
.Xr kvm_getenvv 3 ,
|
.Xr kvm_getenvv 3 ,
|
||||||
.Xr kvm_geterr 3 ,
|
.Xr kvm_geterr 3 ,
|
||||||
.Xr kvm_getprocs 3 ,
|
.Xr kvm_getprocs 3 ,
|
||||||
|
.Xr kvm_native 3 ,
|
||||||
.Xr kvm_open 3 ,
|
.Xr kvm_open 3 ,
|
||||||
.Xr kvm_openfiles 3 ,
|
.Xr kvm_openfiles 3 ,
|
||||||
.Xr kvm_read 3 ,
|
.Xr kvm_read 3 ,
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,12 @@
|
||||||
.\" @(#)kvm_open.3 8.3 (Berkeley) 4/19/94
|
.\" @(#)kvm_open.3 8.3 (Berkeley) 4/19/94
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd January 29, 2004
|
.Dd November 27, 2015
|
||||||
.Dt KVM_OPEN 3
|
.Dt KVM_OPEN 3
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
.Nm kvm_open ,
|
.Nm kvm_open ,
|
||||||
|
.Nm kvm_open2 ,
|
||||||
.Nm kvm_openfiles ,
|
.Nm kvm_openfiles ,
|
||||||
.Nm kvm_close
|
.Nm kvm_close
|
||||||
.Nd initialize kernel virtual memory access
|
.Nd initialize kernel virtual memory access
|
||||||
|
|
@ -48,12 +49,21 @@
|
||||||
.Ft kvm_t *
|
.Ft kvm_t *
|
||||||
.Fn kvm_open "const char *execfile" "const char *corefile" "const char *swapfile" "int flags" "const char *errstr"
|
.Fn kvm_open "const char *execfile" "const char *corefile" "const char *swapfile" "int flags" "const char *errstr"
|
||||||
.Ft kvm_t *
|
.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"
|
.Fn kvm_openfiles "const char *execfile" "const char *corefile" "const char *swapfile" "int flags" "char *errbuf"
|
||||||
.Ft int
|
.Ft int
|
||||||
.Fn kvm_close "kvm_t *kd"
|
.Fn kvm_close "kvm_t *kd"
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
The functions
|
The functions
|
||||||
.Fn kvm_open
|
.Fn kvm_open ,
|
||||||
|
.Fn kvm_open2 ,
|
||||||
and
|
and
|
||||||
.Fn kvm_openfiles
|
.Fn kvm_openfiles
|
||||||
return a descriptor used to access kernel virtual memory
|
return a descriptor used to access kernel virtual memory
|
||||||
|
|
@ -111,10 +121,13 @@ and
|
||||||
.Dv O_RDWR
|
.Dv O_RDWR
|
||||||
are permitted.
|
are permitted.
|
||||||
.Pp
|
.Pp
|
||||||
There are two open routines which differ only with respect to
|
The
|
||||||
the error mechanism.
|
.Nm kvm
|
||||||
|
library provides two different error reporting mechanisms.
|
||||||
One provides backward compatibility with the SunOS kvm library, while the
|
One provides backward compatibility with the SunOS kvm library, while the
|
||||||
other provides an improved error reporting framework.
|
other provides an improved error reporting framework.
|
||||||
|
The mechanism used by a descriptor is determined by the function used to
|
||||||
|
open the descriptor.
|
||||||
.Pp
|
.Pp
|
||||||
The
|
The
|
||||||
.Fn kvm_open
|
.Fn kvm_open
|
||||||
|
|
@ -140,8 +153,10 @@ The string is assumed to persist at least until the corresponding
|
||||||
call.
|
call.
|
||||||
.Pp
|
.Pp
|
||||||
The
|
The
|
||||||
|
.Fn kvm_open2
|
||||||
|
and
|
||||||
.Fn kvm_openfiles
|
.Fn kvm_openfiles
|
||||||
function provides
|
functions provide
|
||||||
.Bx
|
.Bx
|
||||||
style error reporting.
|
style error reporting.
|
||||||
Here, error messages are not printed out by the library.
|
Here, error messages are not printed out by the library.
|
||||||
|
|
@ -160,25 +175,56 @@ on failure,
|
||||||
.Fn kvm_geterr
|
.Fn kvm_geterr
|
||||||
cannot be used to get the error message if open fails.
|
cannot be used to get the error message if open fails.
|
||||||
Thus,
|
Thus,
|
||||||
|
.Fn kvm_open2
|
||||||
|
and
|
||||||
.Fn kvm_openfiles
|
.Fn kvm_openfiles
|
||||||
will place any error message in the
|
will place any error message in the
|
||||||
.Fa errbuf
|
.Fa errbuf
|
||||||
argument.
|
argument.
|
||||||
This buffer should be _POSIX2_LINE_MAX characters large (from
|
This buffer should be _POSIX2_LINE_MAX characters large (from
|
||||||
<limits.h>).
|
<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
|
.Sh RETURN VALUES
|
||||||
The
|
The
|
||||||
.Fn kvm_open
|
.Fn kvm_open ,
|
||||||
|
.Fn kvm_open2 ,
|
||||||
and
|
and
|
||||||
.Fn kvm_openfiles
|
.Fn kvm_openfiles
|
||||||
functions both return a descriptor to be used
|
functions return a descriptor to be used
|
||||||
in all subsequent kvm library calls.
|
in all subsequent kvm library calls.
|
||||||
The library is fully re-entrant.
|
The library is fully re-entrant.
|
||||||
On failure,
|
On failure,
|
||||||
.Dv NULL
|
.Dv NULL
|
||||||
is returned, in which case
|
is returned, in which case
|
||||||
|
.Fn kvm_open2
|
||||||
|
and
|
||||||
.Fn kvm_openfiles
|
.Fn kvm_openfiles
|
||||||
writes the error message into
|
write the error message into
|
||||||
.Fa errbuf .
|
.Fa errbuf .
|
||||||
.Pp
|
.Pp
|
||||||
The
|
The
|
||||||
|
|
@ -191,13 +237,14 @@ function returns 0 on success and -1 on failure.
|
||||||
.Xr kvm_getenvv 3 ,
|
.Xr kvm_getenvv 3 ,
|
||||||
.Xr kvm_geterr 3 ,
|
.Xr kvm_geterr 3 ,
|
||||||
.Xr kvm_getprocs 3 ,
|
.Xr kvm_getprocs 3 ,
|
||||||
|
.Xr kvm_native 3 ,
|
||||||
.Xr kvm_nlist 3 ,
|
.Xr kvm_nlist 3 ,
|
||||||
.Xr kvm_read 3 ,
|
.Xr kvm_read 3 ,
|
||||||
.Xr kvm_write 3 ,
|
.Xr kvm_write 3 ,
|
||||||
.Xr kmem 4 ,
|
.Xr kmem 4 ,
|
||||||
.Xr mem 4
|
.Xr mem 4
|
||||||
.Sh BUGS
|
.Sh BUGS
|
||||||
There should not be two open calls.
|
There should not be three open calls.
|
||||||
The ill-defined error semantics
|
The ill-defined error semantics
|
||||||
of the Sun library and the desire to have a backward-compatible library
|
of the Sun library and the desire to have a backward-compatible library
|
||||||
for
|
for
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,7 @@ _kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error)
|
||||||
static int
|
static int
|
||||||
_kvm_dpcpu_init(kvm_t *kd)
|
_kvm_dpcpu_init(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct nlist nl[] = {
|
struct kvm_nlist nl[] = {
|
||||||
#define NLIST_START_SET_PCPU 0
|
#define NLIST_START_SET_PCPU 0
|
||||||
{ .n_name = "___start_" DPCPU_SETNAME },
|
{ .n_name = "___start_" DPCPU_SETNAME },
|
||||||
#define NLIST_STOP_SET_PCPU 1
|
#define NLIST_STOP_SET_PCPU 1
|
||||||
|
|
@ -231,6 +231,12 @@ _kvm_dpcpu_init(kvm_t *kd)
|
||||||
size_t len;
|
size_t len;
|
||||||
u_int dpcpu_maxcpus;
|
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
|
* Locate and cache locations of important symbols using the internal
|
||||||
* version of _kvm_nlist, turning off initialization to avoid
|
* 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
|
* Check whether the value is within the dpcpu symbol range and only if so
|
||||||
* adjust the offset relative to the current offset.
|
* adjust the offset relative to the current offset.
|
||||||
*/
|
*/
|
||||||
uintptr_t
|
kvaddr_t
|
||||||
_kvm_dpcpu_validaddr(kvm_t *kd, uintptr_t value)
|
_kvm_dpcpu_validaddr(kvm_t *kd, kvaddr_t value)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (value == 0)
|
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)
|
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),
|
return (kvm_read(kd, (uintptr_t)(base + sizeof(struct pcpu) * cpu),
|
||||||
buf, size));
|
buf, size));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,9 @@ __FBSDID("$FreeBSD$");
|
||||||
#include <sys/kerneldump.h>
|
#include <sys/kerneldump.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include <vm/vm.h>
|
|
||||||
|
|
||||||
#include <db.h>
|
|
||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
#include <limits.h>
|
|
||||||
#include <kvm.h>
|
#include <kvm.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
@ -100,7 +97,7 @@ powerpc_maphdrs(kvm_t *kd)
|
||||||
|
|
||||||
vm = kd->vmst;
|
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);
|
vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
||||||
if (vm->map == MAP_FAILED) {
|
if (vm->map == MAP_FAILED) {
|
||||||
_kvm_err(kd, kd->program, "cannot map corefile");
|
_kvm_err(kd, kd->program, "cannot map corefile");
|
||||||
|
|
@ -130,7 +127,7 @@ powerpc_maphdrs(kvm_t *kd)
|
||||||
vm->mapsz = vm->dmphdrsz + mapsz;
|
vm->mapsz = vm->dmphdrsz + mapsz;
|
||||||
vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
||||||
if (vm->map == MAP_FAILED) {
|
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);
|
return (-1);
|
||||||
}
|
}
|
||||||
vm->eh = (void *)((uintptr_t)vm->map + vm->dmphdrsz);
|
vm->eh = (void *)((uintptr_t)vm->map + vm->dmphdrsz);
|
||||||
|
|
@ -138,8 +135,6 @@ powerpc_maphdrs(kvm_t *kd)
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
inval:
|
inval:
|
||||||
munmap(vm->map, vm->mapsz);
|
|
||||||
vm->map = MAP_FAILED;
|
|
||||||
_kvm_err(kd, kd->program, "invalid corefile");
|
_kvm_err(kd, kd->program, "invalid corefile");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +145,7 @@ powerpc_maphdrs(kvm_t *kd)
|
||||||
* 0 when the virtual address is invalid.
|
* 0 when the virtual address is invalid.
|
||||||
*/
|
*/
|
||||||
static size_t
|
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;
|
struct vmstate *vm = kd->vmst;
|
||||||
Elf32_Phdr *ph;
|
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)));
|
return (be32toh(ph->p_memsz) - (va - be32toh(ph->p_vaddr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
_kvm_freevtop(kvm_t *kd)
|
_powerpc_freevtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vm = kd->vmst;
|
struct vmstate *vm = kd->vmst;
|
||||||
|
|
||||||
if (vm == NULL)
|
if (vm->eh != MAP_FAILED)
|
||||||
return;
|
|
||||||
|
|
||||||
if (vm->eh != MAP_FAILED) {
|
|
||||||
munmap(vm->eh, vm->mapsz);
|
munmap(vm->eh, vm->mapsz);
|
||||||
vm->eh = MAP_FAILED;
|
|
||||||
}
|
|
||||||
free(vm);
|
free(vm);
|
||||||
kd->vmst = NULL;
|
kd->vmst = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_initvtop(kvm_t *kd)
|
_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));
|
kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
|
||||||
if (kd->vmst == NULL) {
|
if (kd->vmst == NULL)
|
||||||
_kvm_err(kd, kd->program, "out of virtual memory");
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
|
||||||
if (powerpc_maphdrs(kd) == -1) {
|
if (powerpc_maphdrs(kd) == -1)
|
||||||
free(kd->vmst);
|
|
||||||
kd->vmst = NULL;
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *ofs)
|
_powerpc_kvatop(kvm_t *kd, kvaddr_t va, off_t *ofs)
|
||||||
{
|
{
|
||||||
struct vmstate *vm;
|
struct vmstate *vm;
|
||||||
|
|
||||||
vm = kd->vmst;
|
vm = kd->vmst;
|
||||||
if (vm->ph->p_paddr == ~0U)
|
if (be32toh(vm->ph->p_paddr) == 0xffffffff)
|
||||||
return ((int)powerpc_va2off(kd, va, ofs));
|
return ((int)powerpc_va2off(kd, va, ofs));
|
||||||
|
|
||||||
_kvm_err(kd, kd->program, "Raw corefile not supported");
|
_kvm_err(kd, kd->program, "Raw corefile not supported");
|
||||||
return (0);
|
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/kerneldump.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#include <vm/vm.h>
|
|
||||||
|
|
||||||
#include <db.h>
|
|
||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
#include <limits.h>
|
|
||||||
#include <kvm.h>
|
#include <kvm.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
@ -100,7 +97,7 @@ powerpc_maphdrs(kvm_t *kd)
|
||||||
|
|
||||||
vm = kd->vmst;
|
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);
|
vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
||||||
if (vm->map == MAP_FAILED) {
|
if (vm->map == MAP_FAILED) {
|
||||||
_kvm_err(kd, kd->program, "cannot map corefile");
|
_kvm_err(kd, kd->program, "cannot map corefile");
|
||||||
|
|
@ -130,16 +127,15 @@ powerpc_maphdrs(kvm_t *kd)
|
||||||
vm->mapsz = vm->dmphdrsz + mapsz;
|
vm->mapsz = vm->dmphdrsz + mapsz;
|
||||||
vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
vm->map = mmap(NULL, vm->mapsz, PROT_READ, MAP_PRIVATE, kd->pmfd, 0);
|
||||||
if (vm->map == MAP_FAILED) {
|
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);
|
return (-1);
|
||||||
}
|
}
|
||||||
vm->eh = (void *)((uintptr_t)vm->map + vm->dmphdrsz);
|
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);
|
return (0);
|
||||||
|
|
||||||
inval:
|
inval:
|
||||||
munmap(vm->map, vm->mapsz);
|
|
||||||
vm->map = MAP_FAILED;
|
|
||||||
_kvm_err(kd, kd->program, "invalid corefile");
|
_kvm_err(kd, kd->program, "invalid corefile");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +146,7 @@ powerpc_maphdrs(kvm_t *kd)
|
||||||
* 0 when the virtual address is invalid.
|
* 0 when the virtual address is invalid.
|
||||||
*/
|
*/
|
||||||
static size_t
|
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;
|
struct vmstate *vm = kd->vmst;
|
||||||
Elf64_Phdr *ph;
|
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)));
|
return (be64toh(ph->p_memsz) - (va - be64toh(ph->p_vaddr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
_kvm_freevtop(kvm_t *kd)
|
_powerpc64_freevtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct vmstate *vm = kd->vmst;
|
struct vmstate *vm = kd->vmst;
|
||||||
|
|
||||||
if (vm == NULL)
|
if (vm->eh != MAP_FAILED)
|
||||||
return;
|
|
||||||
|
|
||||||
if (vm->eh != MAP_FAILED) {
|
|
||||||
munmap(vm->eh, vm->mapsz);
|
munmap(vm->eh, vm->mapsz);
|
||||||
vm->eh = MAP_FAILED;
|
|
||||||
}
|
|
||||||
free(vm);
|
free(vm);
|
||||||
kd->vmst = NULL;
|
kd->vmst = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_initvtop(kvm_t *kd)
|
_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));
|
kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
|
||||||
if (kd->vmst == NULL) {
|
if (kd->vmst == NULL)
|
||||||
_kvm_err(kd, kd->program, "out of virtual memory");
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
|
||||||
if (powerpc_maphdrs(kd) == -1) {
|
if (powerpc_maphdrs(kd) == -1)
|
||||||
free(kd->vmst);
|
|
||||||
kd->vmst = NULL;
|
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *ofs)
|
_powerpc64_kvatop(kvm_t *kd, kvaddr_t va, off_t *ofs)
|
||||||
{
|
{
|
||||||
struct vmstate *vm;
|
struct vmstate *vm;
|
||||||
|
|
||||||
vm = kd->vmst;
|
vm = kd->vmst;
|
||||||
if (vm->ph->p_paddr == ~0UL)
|
if (be64toh(vm->ph->p_paddr) == 0xffffffffffffffff)
|
||||||
return ((int)powerpc64_va2off(kd, va, ofs));
|
return ((int)powerpc64_va2off(kd, va, ofs));
|
||||||
|
|
||||||
_kvm_err(kd, kd->program, "Raw corefile not supported");
|
_kvm_err(kd, kd->program, "Raw corefile not supported");
|
||||||
return (0);
|
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$
|
* $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 {
|
||||||
|
struct kvm_arch *arch;
|
||||||
/*
|
/*
|
||||||
* a string to be prepended to error messages
|
* a string to be prepended to error messages
|
||||||
* provided for compatibility with sun's interface
|
* provided for compatibility with sun's interface
|
||||||
|
|
@ -46,8 +61,9 @@ struct __kvm {
|
||||||
#define ISALIVE(kd) ((kd)->vmfd >= 0)
|
#define ISALIVE(kd) ((kd)->vmfd >= 0)
|
||||||
int pmfd; /* physical memory file (or crashdump) */
|
int pmfd; /* physical memory file (or crashdump) */
|
||||||
int vmfd; /* virtual memory file (-1 if 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) */
|
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;
|
struct kinfo_proc *procbase;
|
||||||
char *argspc; /* (dynamic) storage for argv strings */
|
char *argspc; /* (dynamic) storage for argv strings */
|
||||||
int arglen; /* length of the above */
|
int arglen; /* length of the above */
|
||||||
|
|
@ -64,10 +80,10 @@ struct __kvm {
|
||||||
int rawdump; /* raw dump format */
|
int rawdump; /* raw dump format */
|
||||||
|
|
||||||
int vnet_initialized; /* vnet fields set up */
|
int vnet_initialized; /* vnet fields set up */
|
||||||
uintptr_t vnet_start; /* start of kernel's vnet region */
|
kvaddr_t vnet_start; /* start of kernel's vnet region */
|
||||||
uintptr_t vnet_stop; /* stop of kernel's vnet region */
|
kvaddr_t vnet_stop; /* stop of kernel's vnet region */
|
||||||
uintptr_t vnet_current; /* vnet we're working with */
|
kvaddr_t vnet_current; /* vnet we're working with */
|
||||||
uintptr_t vnet_base; /* vnet base of current vnet */
|
kvaddr_t vnet_base; /* vnet base of current vnet */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dynamic per-CPU kernel memory. We translate symbols, on-demand,
|
* Dynamic per-CPU kernel memory. We translate symbols, on-demand,
|
||||||
|
|
@ -75,38 +91,69 @@ struct __kvm {
|
||||||
* kvm_dpcpu_setcpu().
|
* kvm_dpcpu_setcpu().
|
||||||
*/
|
*/
|
||||||
int dpcpu_initialized; /* dpcpu fields set up */
|
int dpcpu_initialized; /* dpcpu fields set up */
|
||||||
uintptr_t dpcpu_start; /* start of kernel's dpcpu region */
|
kvaddr_t dpcpu_start; /* start of kernel's dpcpu region */
|
||||||
uintptr_t dpcpu_stop; /* stop of kernel's dpcpu region */
|
kvaddr_t dpcpu_stop; /* stop of kernel's dpcpu region */
|
||||||
u_int dpcpu_maxcpus; /* size of base array */
|
u_int dpcpu_maxcpus; /* size of base array */
|
||||||
uintptr_t *dpcpu_off; /* base array, indexed by CPU ID */
|
uintptr_t *dpcpu_off; /* base array, indexed by CPU ID */
|
||||||
u_int dpcpu_curcpu; /* CPU we're currently working with */
|
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.
|
* 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, ...)
|
void _kvm_err(kvm_t *kd, const char *program, const char *fmt, ...)
|
||||||
__printflike(3, 4);
|
__printflike(3, 4);
|
||||||
void _kvm_freeprocs(kvm_t *kd);
|
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);
|
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_realloc(kvm_t *kd, void *, size_t);
|
||||||
void _kvm_syserr (kvm_t *kd, const char *program, const char *fmt, ...)
|
void _kvm_syserr (kvm_t *kd, const char *program, const char *fmt, ...)
|
||||||
__printflike(3, 4);
|
__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_selectpid(kvm_t *, pid_t);
|
||||||
int _kvm_vnet_initialized(kvm_t *, int);
|
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);
|
int _kvm_dpcpu_initialized(kvm_t *, int);
|
||||||
uintptr_t _kvm_dpcpu_validaddr(kvm_t *, uintptr_t);
|
kvaddr_t _kvm_dpcpu_validaddr(kvm_t *, kvaddr_t);
|
||||||
|
int _kvm_probe_elf_kernel(kvm_t *, int, int);
|
||||||
#if defined(__aarch64__) || defined(__amd64__) || defined(__arm__) || \
|
int _kvm_is_minidump(kvm_t *);
|
||||||
defined(__i386__) || defined(__mips__)
|
int _kvm_read_core_phdrs(kvm_t *, size_t *, GElf_Phdr **);
|
||||||
void _kvm_minidump_freevtop(kvm_t *);
|
void _kvm_hpt_init(kvm_t *, struct hpt *, void *, size_t, off_t, int, int);
|
||||||
int _kvm_minidump_initvtop(kvm_t *);
|
off_t _kvm_hpt_find(struct hpt *, uint64_t);
|
||||||
int _kvm_minidump_kvatop(kvm_t *, u_long, off_t *);
|
void _kvm_hpt_free(struct hpt *);
|
||||||
#endif
|
|
||||||
|
|
|
||||||
|
|
@ -582,6 +582,12 @@ liveout:
|
||||||
nl[5].n_name = "_cpu_tick_frequency";
|
nl[5].n_name = "_cpu_tick_frequency";
|
||||||
nl[6].n_name = 0;
|
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) {
|
if (kvm_nlist(kd, nl) != 0) {
|
||||||
for (p = nl; p->n_type != 0; ++p)
|
for (p = nl; p->n_type != 0; ++p)
|
||||||
;
|
;
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,12 @@
|
||||||
.\" @(#)kvm_read.3 8.1 (Berkeley) 6/4/93
|
.\" @(#)kvm_read.3 8.1 (Berkeley) 6/4/93
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd June 4, 1993
|
.Dd November 27, 2015
|
||||||
.Dt KVM_READ 3
|
.Dt KVM_READ 3
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
.Nm kvm_read ,
|
.Nm kvm_read ,
|
||||||
|
.Nm kvm_read2 ,
|
||||||
.Nm kvm_write
|
.Nm kvm_write
|
||||||
.Nd read or write kernel virtual memory
|
.Nd read or write kernel virtual memory
|
||||||
.Sh LIBRARY
|
.Sh LIBRARY
|
||||||
|
|
@ -46,23 +47,26 @@
|
||||||
.Ft ssize_t
|
.Ft ssize_t
|
||||||
.Fn kvm_read "kvm_t *kd" "unsigned long addr" "void *buf" "size_t nbytes"
|
.Fn kvm_read "kvm_t *kd" "unsigned long addr" "void *buf" "size_t nbytes"
|
||||||
.Ft ssize_t
|
.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"
|
.Fn kvm_write "kvm_t *kd" "unsigned long addr" "const void *buf" "size_t nbytes"
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
The
|
The
|
||||||
.Fn kvm_read
|
.Fn kvm_read ,
|
||||||
|
.Fn kvm_read2 ,
|
||||||
and
|
and
|
||||||
.Fn kvm_write
|
.Fn kvm_write
|
||||||
functions are used to read and write kernel virtual memory (or a crash
|
functions are used to read and write kernel virtual memory (or a crash
|
||||||
dump file).
|
dump file).
|
||||||
See
|
See
|
||||||
.Fn kvm_open 3
|
.Fn kvm_open 3
|
||||||
or
|
|
||||||
.Fn kvm_openfiles 3
|
|
||||||
for information regarding opening kernel virtual memory and crash dumps.
|
for information regarding opening kernel virtual memory and crash dumps.
|
||||||
.Pp
|
.Pp
|
||||||
The
|
The
|
||||||
.Fn kvm_read
|
.Fn kvm_read
|
||||||
function transfers
|
and
|
||||||
|
.Fn kvm_read2
|
||||||
|
functions transfer
|
||||||
.Fa nbytes
|
.Fa nbytes
|
||||||
bytes of data from
|
bytes of data from
|
||||||
the kernel space address
|
the kernel space address
|
||||||
|
|
@ -77,6 +81,16 @@ to
|
||||||
.Fa addr .
|
.Fa addr .
|
||||||
Unlike their SunOS counterparts, these functions cannot be used to
|
Unlike their SunOS counterparts, these functions cannot be used to
|
||||||
read or write process address spaces.
|
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
|
.Sh RETURN VALUES
|
||||||
Upon success, the number of bytes actually transferred is returned.
|
Upon success, the number of bytes actually transferred is returned.
|
||||||
Otherwise, -1 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/param.h>
|
||||||
#include <sys/user.h>
|
#include <kvm.h>
|
||||||
#include <sys/proc.h>
|
#include <limits.h>
|
||||||
#include <sys/stat.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <nlist.h>
|
|
||||||
#include <kvm.h>
|
|
||||||
|
|
||||||
#include <vm/vm.h>
|
#include "../../sys/sparc64/include/kerneldump.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 "kvm_private.h"
|
#include "kvm_private.h"
|
||||||
|
#include "kvm_sparc64.h"
|
||||||
#ifndef btop
|
|
||||||
#define btop(x) (sparc64_btop(x))
|
|
||||||
#define ptob(x) (sparc64_ptob(x))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct vmstate {
|
struct vmstate {
|
||||||
off_t vm_tsb_off;
|
off_t vm_tsb_off;
|
||||||
vm_size_t vm_tsb_mask;
|
uint64_t vm_tsb_mask;
|
||||||
int vm_nregions;
|
int vm_nregions;
|
||||||
struct sparc64_dump_reg *vm_regions;
|
struct sparc64_dump_reg *vm_regions;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
static int
|
||||||
_kvm_freevtop(kvm_t *kd)
|
_sparc64_probe(kvm_t *kd)
|
||||||
{
|
{
|
||||||
if (kd->vmst != 0) {
|
|
||||||
free(kd->vmst->vm_regions);
|
return (_kvm_probe_elf_kernel(kd, ELFCLASS64, EM_SPARCV9));
|
||||||
free(kd->vmst);
|
}
|
||||||
}
|
|
||||||
|
static void
|
||||||
|
_sparc64_freevtop(kvm_t *kd)
|
||||||
|
{
|
||||||
|
|
||||||
|
free(kd->vmst->vm_regions);
|
||||||
|
free(kd->vmst);
|
||||||
|
kd->vmst = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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. */
|
/* XXX This has to be a raw file read, kvm_read is virtual. */
|
||||||
if (lseek(kd->pmfd, pos, SEEK_SET) == -1) {
|
if (pread(kd->pmfd, buf, size, pos) != (ssize_t)size) {
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_read_phys: lseek");
|
_kvm_syserr(kd, kd->program, "_sparc64_read_phys: pread");
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
if (read(kd->pmfd, buf, size) != (ssize_t)size) {
|
|
||||||
_kvm_syserr(kd, kd->program, "_kvm_read_phys: read");
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
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
|
#define KVM_OFF_NOTFOUND 0
|
||||||
|
|
||||||
static off_t
|
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;
|
struct sparc64_dump_reg *reg, key;
|
||||||
vm_offset_t o;
|
vm_offset_t o;
|
||||||
|
|
||||||
key.dr_pa = pa;
|
key.dr_pa = pa;
|
||||||
reg = bsearch(&key, vm->vm_regions, vm->vm_nregions,
|
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)
|
if (reg == NULL)
|
||||||
return (KVM_OFF_NOTFOUND);
|
return (KVM_OFF_NOTFOUND);
|
||||||
o = pa - reg->dr_pa;
|
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);
|
return (reg->dr_offs + o);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_initvtop(kvm_t *kd)
|
_sparc64_initvtop(kvm_t *kd)
|
||||||
{
|
{
|
||||||
struct sparc64_dump_hdr hdr;
|
struct sparc64_dump_hdr hdr;
|
||||||
struct sparc64_dump_reg *regs;
|
struct sparc64_dump_reg *regs;
|
||||||
struct vmstate *vm;
|
struct vmstate *vm;
|
||||||
size_t regsz;
|
size_t regsz;
|
||||||
vm_offset_t pa;
|
uint64_t pa;
|
||||||
|
int i;
|
||||||
|
|
||||||
vm = (struct vmstate *)_kvm_malloc(kd, sizeof(*vm));
|
vm = (struct vmstate *)_kvm_malloc(kd, sizeof(*vm));
|
||||||
if (vm == NULL) {
|
if (vm == NULL) {
|
||||||
|
|
@ -154,8 +144,13 @@ _kvm_initvtop(kvm_t *kd)
|
||||||
}
|
}
|
||||||
kd->vmst = vm;
|
kd->vmst = vm;
|
||||||
|
|
||||||
if (!_kvm_read_phys(kd, 0, &hdr, sizeof(hdr)))
|
if (!_sparc64_read_phys(kd, 0, &hdr, sizeof(hdr)))
|
||||||
goto fail_vm;
|
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;
|
pa = hdr.dh_tsb_pa;
|
||||||
|
|
||||||
regsz = hdr.dh_nregions * sizeof(*regs);
|
regsz = hdr.dh_nregions * sizeof(*regs);
|
||||||
|
|
@ -164,14 +159,19 @@ _kvm_initvtop(kvm_t *kd)
|
||||||
_kvm_err(kd, kd->program, "cannot allocate regions");
|
_kvm_err(kd, kd->program, "cannot allocate regions");
|
||||||
goto fail_vm;
|
goto fail_vm;
|
||||||
}
|
}
|
||||||
if (!_kvm_read_phys(kd, sizeof(hdr), regs, regsz))
|
if (!_sparc64_read_phys(kd, sizeof(hdr), regs, regsz))
|
||||||
goto fail_regs;
|
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_tsb_mask = hdr.dh_tsb_mask;
|
||||||
vm->vm_regions = regs;
|
vm->vm_regions = regs;
|
||||||
vm->vm_nregions = hdr.dh_nregions;
|
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) {
|
if (vm->vm_tsb_off == KVM_OFF_NOTFOUND) {
|
||||||
_kvm_err(kd, kd->program, "tsb not found in dump");
|
_kvm_err(kd, kd->program, "tsb not found in dump");
|
||||||
goto fail_regs;
|
goto fail_regs;
|
||||||
|
|
@ -185,37 +185,60 @@ fail_vm:
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
|
_sparc64_kvatop(kvm_t *kd, kvaddr_t va, off_t *pa)
|
||||||
{
|
{
|
||||||
struct tte tte;
|
struct sparc64_tte tte;
|
||||||
off_t tte_off;
|
off_t tte_off;
|
||||||
u_long vpn;
|
kvaddr_t vpn;
|
||||||
off_t pa_off;
|
off_t pa_off;
|
||||||
u_long pg_off;
|
kvaddr_t pg_off;
|
||||||
int rest;
|
int rest;
|
||||||
|
|
||||||
pg_off = va & PAGE_MASK;
|
pg_off = va & SPARC64_PAGE_MASK;
|
||||||
if (va >= VM_MIN_DIRECT_ADDRESS)
|
if (va >= SPARC64_MIN_DIRECT_ADDRESS)
|
||||||
pa_off = TLB_DIRECT_TO_PHYS(va) & ~PAGE_MASK;
|
pa_off = SPARC64_DIRECT_TO_PHYS(va) & ~SPARC64_PAGE_MASK;
|
||||||
else {
|
else {
|
||||||
vpn = btop(va);
|
vpn = va >> SPARC64_PAGE_SHIFT;
|
||||||
tte_off = kd->vmst->vm_tsb_off +
|
tte_off = kd->vmst->vm_tsb_off +
|
||||||
((vpn & kd->vmst->vm_tsb_mask) << TTE_SHIFT);
|
((vpn & kd->vmst->vm_tsb_mask) << SPARC64_TTE_SHIFT);
|
||||||
if (!_kvm_read_phys(kd, tte_off, &tte, sizeof(tte)))
|
if (!_sparc64_read_phys(kd, tte_off, &tte, sizeof(tte)))
|
||||||
goto invalid;
|
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;
|
goto invalid;
|
||||||
pa_off = TTE_GET_PA(&tte);
|
pa_off = SPARC64_TTE_GET_PA(&tte);
|
||||||
}
|
}
|
||||||
rest = PAGE_SIZE - pg_off;
|
rest = SPARC64_PAGE_SIZE - pg_off;
|
||||||
pa_off = _kvm_find_off(kd->vmst, pa_off, rest);
|
pa_off = _sparc64_find_off(kd->vmst, pa_off, rest);
|
||||||
if (pa_off == KVM_OFF_NOTFOUND)
|
if (pa_off == KVM_OFF_NOTFOUND)
|
||||||
goto invalid;
|
goto invalid;
|
||||||
*pa = pa_off + pg_off;
|
*pa = pa_off + pg_off;
|
||||||
return (rest);
|
return (rest);
|
||||||
|
|
||||||
invalid:
|
invalid:
|
||||||
_kvm_err(kd, 0, "invalid address (%lx)", va);
|
_kvm_err(kd, 0, "invalid address (%jx)", (uintmax_t)va);
|
||||||
return (0);
|
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 <net/vnet.h>
|
||||||
|
|
||||||
#include <nlist.h>
|
|
||||||
#include <kvm.h>
|
#include <kvm.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
@ -62,7 +61,7 @@ _kvm_vnet_selectpid(kvm_t *kd, pid_t pid)
|
||||||
struct ucred cred;
|
struct ucred cred;
|
||||||
struct prison prison;
|
struct prison prison;
|
||||||
struct vnet vnet;
|
struct vnet vnet;
|
||||||
struct nlist nl[] = {
|
struct kvm_nlist nl[] = {
|
||||||
/*
|
/*
|
||||||
* Note: kvm_nlist strips the first '_' so add an extra one
|
* Note: kvm_nlist strips the first '_' so add an extra one
|
||||||
* here to __{start,stop}_set_vnet.
|
* here to __{start,stop}_set_vnet.
|
||||||
|
|
@ -89,6 +88,12 @@ _kvm_vnet_selectpid(kvm_t *kd, pid_t pid)
|
||||||
#endif
|
#endif
|
||||||
lwpid_t dumptid;
|
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
|
* Locate and cache locations of important symbols
|
||||||
* using the internal version of _kvm_nlist, turning
|
* 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
|
* Check whether the vnet module has been initialized sucessfully
|
||||||
* or not, intialize it if permitted.
|
* or not, initialize it if permitted.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
_kvm_vnet_initialized(kvm_t *kd, int intialize)
|
_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
|
* Check whether the value is within the vnet symbol range and
|
||||||
* only if so adjust the offset relative to the current base.
|
* only if so adjust the offset relative to the current base.
|
||||||
*/
|
*/
|
||||||
uintptr_t
|
kvaddr_t
|
||||||
_kvm_vnet_validaddr(kvm_t *kd, uintptr_t value)
|
_kvm_vnet_validaddr(kvm_t *kd, kvaddr_t value)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (value == 0)
|
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 \
|
ed expr getfacl hostname kenv kill ln ls mkdir mv \
|
||||||
pkill ps pwd realpath rm rmdir setfacl sh sleep stty \
|
pkill ps pwd realpath rm rmdir setfacl sh sleep stty \
|
||||||
sync test
|
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
|
CRUNCH_BUILDTOOLS+= bin/sh
|
||||||
|
|
||||||
# Additional options for specific programs
|
# Additional options for specific programs
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,7 @@ _DP_geom= bsdxml sbuf
|
||||||
_DP_cam= sbuf
|
_DP_cam= sbuf
|
||||||
_DP_casper= capsicum nv pjdlog
|
_DP_casper= capsicum nv pjdlog
|
||||||
_DP_capsicum= nv
|
_DP_capsicum= nv
|
||||||
|
_DP_kvm= elf
|
||||||
_DP_pjdlog= util
|
_DP_pjdlog= util
|
||||||
_DP_opie= md
|
_DP_opie= md
|
||||||
_DP_usb= pthread
|
_DP_usb= pthread
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,9 @@
|
||||||
#define _MACHINE_KERNELDUMP_H_
|
#define _MACHINE_KERNELDUMP_H_
|
||||||
|
|
||||||
struct sparc64_dump_reg {
|
struct sparc64_dump_reg {
|
||||||
vm_paddr_t dr_pa;
|
uint64_t dr_pa;
|
||||||
vm_offset_t dr_size;
|
uint64_t dr_size;
|
||||||
vm_offset_t dr_offs;
|
uint64_t dr_offs;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -40,11 +40,12 @@ struct sparc64_dump_reg {
|
||||||
* would require some ugly hacks.
|
* would require some ugly hacks.
|
||||||
*/
|
*/
|
||||||
struct sparc64_dump_hdr {
|
struct sparc64_dump_hdr {
|
||||||
vm_offset_t dh_hdr_size;
|
uint64_t dh_hdr_size;
|
||||||
vm_paddr_t dh_tsb_pa;
|
uint64_t dh_tsb_pa;
|
||||||
vm_size_t dh_tsb_size;
|
uint64_t dh_tsb_size;
|
||||||
vm_size_t dh_tsb_mask;
|
uint64_t dh_tsb_mask;
|
||||||
int dh_nregions;
|
int32_t dh_nregions;
|
||||||
|
int32_t dh_pad;
|
||||||
struct sparc64_dump_reg dh_regions[];
|
struct sparc64_dump_reg dh_regions[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue