2012-08-27 03:09:42 +04:00
|
|
|
/* $NetBSD: kvm.c,v 1.100 2012/08/26 23:09:42 martin Exp $ */
|
1996-03-19 01:33:07 +03:00
|
|
|
|
1993-03-21 12:45:37 +03:00
|
|
|
/*-
|
1994-05-09 07:17:28 +04:00
|
|
|
* Copyright (c) 1989, 1992, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software developed by the Computer Systems
|
|
|
|
* Engineering group at Lawrence Berkeley Laboratory under DARPA contract
|
|
|
|
* BG 91-66 and contributed to Berkeley.
|
1993-03-21 12:45:37 +03:00
|
|
|
*
|
|
|
|
* 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.
|
2003-08-07 20:42:00 +04:00
|
|
|
* 3. Neither the name of the University nor the names of its contributors
|
1993-03-21 12:45:37 +03:00
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
|
|
|
|
*/
|
|
|
|
|
1997-08-15 06:21:56 +04:00
|
|
|
#include <sys/cdefs.h>
|
1993-03-21 12:45:37 +03:00
|
|
|
#if defined(LIBC_SCCS) && !defined(lint)
|
1996-03-19 01:33:07 +03:00
|
|
|
#if 0
|
1994-05-09 07:17:28 +04:00
|
|
|
static char sccsid[] = "@(#)kvm.c 8.2 (Berkeley) 2/13/94";
|
1996-03-19 01:33:07 +03:00
|
|
|
#else
|
2012-08-27 03:09:42 +04:00
|
|
|
__RCSID("$NetBSD: kvm.c,v 1.100 2012/08/26 23:09:42 martin Exp $");
|
1996-03-19 01:33:07 +03:00
|
|
|
#endif
|
1993-03-21 12:45:37 +03:00
|
|
|
#endif /* LIBC_SCCS and not lint */
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/user.h>
|
2003-01-18 13:32:11 +03:00
|
|
|
#include <sys/lwp.h>
|
1993-03-21 12:45:37 +03:00
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/ioctl.h>
|
1994-05-09 07:17:28 +04:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
|
1996-03-16 13:23:29 +03:00
|
|
|
#include <sys/core.h>
|
2009-08-20 15:18:55 +04:00
|
|
|
#include <sys/exec.h>
|
1996-03-16 13:23:29 +03:00
|
|
|
#include <sys/kcore.h>
|
2003-05-11 17:37:34 +04:00
|
|
|
#include <sys/ksyms.h>
|
Change kvm_pa2off() and kvm_kvatop() prototypes (private to kvm(3)):
-int _kvm_kvatop(kvm_t *, u_long, u_long *);
-off_t _kvm_pa2off(kvm_t *, u_long);
+int _kvm_kvatop(kvm_t *, vaddr_t, paddr_t *);
+off_t _kvm_pa2off(kvm_t *, paddr_t);
Basically, use vaddr_t for VA and paddr_t for PA. In addition, for variables
representing addresses, use paddr_t or vaddr_t, depending on the context.
For most arches, vaddr_t and paddr_t are equivalent to unsigned long. However,
the change was needed for exotic situations, like i386 PAE, were unsigned long
is not suitable for PA which are 64 bits long. As this required a complete
change of the function prototypes, all arches had to be adapted accordingly.
Core files from before this commit should still work with the new code; I did
not see any direct dependency between core's structure and kvatop/pa2off.
The change was compile tested for all arches, as it impacts all of them.
See also:
http://mail-index.netbsd.org/current-users/2010/09/07/msg014249.html
2010-09-21 03:23:16 +04:00
|
|
|
#include <sys/types.h>
|
1996-03-16 13:23:29 +03:00
|
|
|
|
2000-06-29 10:34:22 +04:00
|
|
|
#include <uvm/uvm_extern.h>
|
1994-05-09 07:17:28 +04:00
|
|
|
|
2002-09-18 00:34:08 +04:00
|
|
|
#include <machine/cpu.h>
|
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
#include <ctype.h>
|
2006-05-11 16:00:20 +04:00
|
|
|
#include <errno.h>
|
1993-03-21 12:45:37 +03:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <limits.h>
|
1994-05-09 07:17:28 +04:00
|
|
|
#include <nlist.h>
|
1993-03-21 12:45:37 +03:00
|
|
|
#include <paths.h>
|
2002-05-26 22:33:25 +04:00
|
|
|
#include <stdarg.h>
|
1993-03-21 12:45:37 +03:00
|
|
|
#include <stdio.h>
|
1994-05-09 07:17:28 +04:00
|
|
|
#include <stdlib.h>
|
1993-03-21 12:45:37 +03:00
|
|
|
#include <string.h>
|
1994-05-09 07:17:28 +04:00
|
|
|
#include <unistd.h>
|
1996-03-19 00:14:44 +03:00
|
|
|
#include <kvm.h>
|
1993-03-21 12:45:37 +03:00
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
#include "kvm_private.h"
|
1993-03-21 12:45:37 +03:00
|
|
|
|
2007-11-08 20:32:30 +03:00
|
|
|
static int _kvm_get_header(kvm_t *);
|
|
|
|
static kvm_t *_kvm_open(kvm_t *, const char *, const char *,
|
|
|
|
const char *, int, char *);
|
2007-11-08 23:48:04 +03:00
|
|
|
static int clear_gap(kvm_t *, bool (*)(void *, const void *, size_t),
|
|
|
|
void *, size_t);
|
2007-11-08 20:32:30 +03:00
|
|
|
static off_t Lseek(kvm_t *, int, off_t, int);
|
|
|
|
static ssize_t Pread(kvm_t *, int, void *, size_t, off_t);
|
1994-02-14 16:46:01 +03:00
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
char *
|
2007-11-08 20:32:30 +03:00
|
|
|
kvm_geterr(kvm_t *kd)
|
1994-05-09 07:17:28 +04:00
|
|
|
{
|
|
|
|
return (kd->errbuf);
|
|
|
|
}
|
1994-03-02 01:14:13 +03:00
|
|
|
|
2011-09-13 01:11:31 +04:00
|
|
|
const char *
|
|
|
|
kvm_getkernelname(kvm_t *kd)
|
|
|
|
{
|
|
|
|
return kd->kernelname;
|
|
|
|
}
|
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
/*
|
|
|
|
* Report an error using printf style arguments. "program" is kd->program
|
|
|
|
* on hard errors, and 0 on soft errors, so that under sun error emulation,
|
|
|
|
* only hard errors are printed out (otherwise, programs like gdb will
|
|
|
|
* generate tons of error messages when trying to access bogus pointers).
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
_kvm_err(kvm_t *kd, const char *program, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
1994-02-14 16:46:01 +03:00
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
va_start(ap, fmt);
|
|
|
|
if (program != NULL) {
|
|
|
|
(void)fprintf(stderr, "%s: ", program);
|
|
|
|
(void)vfprintf(stderr, fmt, ap);
|
|
|
|
(void)fputc('\n', stderr);
|
|
|
|
} else
|
|
|
|
(void)vsnprintf(kd->errbuf,
|
1998-08-02 01:29:41 +04:00
|
|
|
sizeof(kd->errbuf), fmt, ap);
|
1994-02-14 16:46:01 +03:00
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
va_end(ap);
|
|
|
|
}
|
1993-03-21 12:45:37 +03:00
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
void
|
|
|
|
_kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
1994-05-09 07:17:28 +04:00
|
|
|
va_list ap;
|
1998-08-02 01:29:41 +04:00
|
|
|
size_t n;
|
1993-03-21 12:45:37 +03:00
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
va_start(ap, fmt);
|
|
|
|
if (program != NULL) {
|
|
|
|
(void)fprintf(stderr, "%s: ", program);
|
|
|
|
(void)vfprintf(stderr, fmt, ap);
|
|
|
|
(void)fprintf(stderr, ": %s\n", strerror(errno));
|
1993-03-21 12:45:37 +03:00
|
|
|
} else {
|
1998-02-03 22:12:13 +03:00
|
|
|
char *cp = kd->errbuf;
|
1994-05-09 07:17:28 +04:00
|
|
|
|
1998-08-02 01:29:41 +04:00
|
|
|
(void)vsnprintf(cp, sizeof(kd->errbuf), fmt, ap);
|
1994-05-09 07:17:28 +04:00
|
|
|
n = strlen(cp);
|
|
|
|
(void)snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s",
|
|
|
|
strerror(errno));
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
va_end(ap);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
void *
|
2007-11-08 20:32:30 +03:00
|
|
|
_kvm_malloc(kvm_t *kd, size_t n)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
1994-05-09 07:17:28 +04:00
|
|
|
void *p;
|
1993-03-21 12:45:37 +03:00
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
if ((p = malloc(n)) == NULL)
|
2000-10-04 20:11:27 +04:00
|
|
|
_kvm_err(kd, kd->program, "%s", strerror(errno));
|
1994-05-09 07:17:28 +04:00
|
|
|
return (p);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
1996-03-16 13:23:29 +03:00
|
|
|
/*
|
1998-07-01 00:40:44 +04:00
|
|
|
* Wrapper around the lseek(2) system call; calls _kvm_syserr() for us
|
|
|
|
* in the event of emergency.
|
1996-03-16 13:23:29 +03:00
|
|
|
*/
|
|
|
|
static off_t
|
2007-11-08 20:32:30 +03:00
|
|
|
Lseek(kvm_t *kd, int fd, off_t offset, int whence)
|
1996-03-16 13:23:29 +03:00
|
|
|
{
|
1998-07-01 00:40:44 +04:00
|
|
|
off_t off;
|
1996-03-16 13:23:29 +03:00
|
|
|
|
|
|
|
errno = 0;
|
1998-07-01 00:40:44 +04:00
|
|
|
|
1996-03-16 13:23:29 +03:00
|
|
|
if ((off = lseek(fd, offset, whence)) == -1 && errno != 0) {
|
|
|
|
_kvm_syserr(kd, kd->program, "Lseek");
|
1998-07-01 00:40:44 +04:00
|
|
|
return ((off_t)-1);
|
1996-03-16 13:23:29 +03:00
|
|
|
}
|
|
|
|
return (off);
|
|
|
|
}
|
|
|
|
|
2008-01-15 16:57:41 +03:00
|
|
|
ssize_t
|
|
|
|
_kvm_pread(kvm_t *kd, int fd, void *buf, size_t size, off_t off)
|
|
|
|
{
|
|
|
|
ptrdiff_t moff;
|
|
|
|
void *newbuf;
|
|
|
|
size_t dsize;
|
|
|
|
ssize_t rv;
|
|
|
|
off_t doff;
|
|
|
|
|
|
|
|
/* If aligned nothing to do. */
|
|
|
|
if (((off % kd->fdalign) | (size % kd->fdalign)) == 0) {
|
|
|
|
return pread(fd, buf, size, off);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise must buffer. We can't tolerate short reads in this
|
|
|
|
* case (lazy bum).
|
|
|
|
*/
|
|
|
|
moff = (ptrdiff_t)off % kd->fdalign;
|
|
|
|
doff = off - moff;
|
|
|
|
dsize = moff + size + kd->fdalign - 1;
|
|
|
|
dsize -= dsize % kd->fdalign;
|
|
|
|
if (kd->iobufsz < dsize) {
|
|
|
|
newbuf = realloc(kd->iobuf, dsize);
|
|
|
|
if (newbuf == NULL) {
|
|
|
|
_kvm_syserr(kd, 0, "cannot allocate I/O buffer");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
kd->iobuf = newbuf;
|
|
|
|
kd->iobufsz = dsize;
|
|
|
|
}
|
|
|
|
rv = pread(fd, kd->iobuf, dsize, doff);
|
2010-10-23 18:34:12 +04:00
|
|
|
if (rv < size + moff)
|
2008-01-15 16:57:41 +03:00
|
|
|
return -1;
|
|
|
|
memcpy(buf, kd->iobuf + moff, size);
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
1998-07-01 00:40:44 +04:00
|
|
|
/*
|
|
|
|
* Wrapper around the pread(2) system call; calls _kvm_syserr() for us
|
|
|
|
* in the event of emergency.
|
|
|
|
*/
|
1996-03-16 13:23:29 +03:00
|
|
|
static ssize_t
|
2007-11-08 20:32:30 +03:00
|
|
|
Pread(kvm_t *kd, int fd, void *buf, size_t nbytes, off_t offset)
|
1996-03-16 13:23:29 +03:00
|
|
|
{
|
1998-07-01 00:40:44 +04:00
|
|
|
ssize_t rv;
|
1996-03-16 13:23:29 +03:00
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
2008-01-15 16:57:41 +03:00
|
|
|
if ((rv = _kvm_pread(kd, fd, buf, nbytes, offset)) != nbytes &&
|
1998-07-01 00:40:44 +04:00
|
|
|
errno != 0)
|
|
|
|
_kvm_syserr(kd, kd->program, "Pread");
|
1996-03-16 13:23:29 +03:00
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
static kvm_t *
|
2007-11-08 20:32:30 +03:00
|
|
|
_kvm_open(kvm_t *kd, const char *uf, const char *mf, const char *sf, int flag,
|
|
|
|
char *errout)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
1994-05-09 07:17:28 +04:00
|
|
|
struct stat st;
|
1996-10-12 04:50:11 +04:00
|
|
|
int ufgiven;
|
1994-05-09 07:17:28 +04:00
|
|
|
|
|
|
|
kd->pmfd = -1;
|
1995-01-09 11:59:25 +03:00
|
|
|
kd->vmfd = -1;
|
1994-05-09 07:17:28 +04:00
|
|
|
kd->swfd = -1;
|
|
|
|
kd->nlfd = -1;
|
2000-05-26 06:42:21 +04:00
|
|
|
kd->alive = KVM_ALIVE_DEAD;
|
2006-02-16 23:48:42 +03:00
|
|
|
kd->procbase = NULL;
|
|
|
|
kd->procbase_len = 0;
|
|
|
|
kd->procbase2 = NULL;
|
|
|
|
kd->procbase2_len = 0;
|
|
|
|
kd->lwpbase = NULL;
|
|
|
|
kd->lwpbase_len = 0;
|
1994-09-18 05:52:01 +04:00
|
|
|
kd->nbpg = getpagesize();
|
2006-02-16 23:48:42 +03:00
|
|
|
kd->swapspc = NULL;
|
|
|
|
kd->argspc = NULL;
|
|
|
|
kd->argspc_len = 0;
|
|
|
|
kd->argbuf = NULL;
|
|
|
|
kd->argv = NULL;
|
|
|
|
kd->vmst = NULL;
|
|
|
|
kd->vm_page_buckets = NULL;
|
|
|
|
kd->kcore_hdr = NULL;
|
1996-05-05 08:31:59 +04:00
|
|
|
kd->cpu_dsize = 0;
|
2006-02-16 23:48:42 +03:00
|
|
|
kd->cpu_data = NULL;
|
1996-03-16 13:23:29 +03:00
|
|
|
kd->dump_off = 0;
|
2008-01-15 16:57:41 +03:00
|
|
|
kd->fdalign = 1;
|
|
|
|
kd->iobuf = NULL;
|
|
|
|
kd->iobufsz = 0;
|
1994-05-09 07:17:28 +04:00
|
|
|
|
2000-05-26 06:42:21 +04:00
|
|
|
if (flag & KVM_NO_FILES) {
|
|
|
|
kd->alive = KVM_ALIVE_SYSCTL;
|
|
|
|
return(kd);
|
|
|
|
}
|
|
|
|
|
1997-08-12 20:28:32 +04:00
|
|
|
/*
|
|
|
|
* Call the MD open hook. This sets:
|
|
|
|
* usrstack, min_uva, max_uva
|
|
|
|
*/
|
|
|
|
if (_kvm_mdopen(kd)) {
|
|
|
|
_kvm_err(kd, kd->program, "md init failed");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
1996-10-12 04:50:11 +04:00
|
|
|
ufgiven = (uf != NULL);
|
2002-09-18 00:34:08 +04:00
|
|
|
if (!ufgiven) {
|
|
|
|
#ifdef CPU_BOOTED_KERNEL
|
|
|
|
/* 130 is 128 + '/' + '\0' */
|
|
|
|
static char booted_kernel[130];
|
|
|
|
int mib[2], rc;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
mib[0] = CTL_MACHDEP;
|
|
|
|
mib[1] = CPU_BOOTED_KERNEL;
|
|
|
|
booted_kernel[0] = '/';
|
|
|
|
booted_kernel[1] = '\0';
|
|
|
|
len = sizeof(booted_kernel) - 2;
|
|
|
|
rc = sysctl(&mib[0], 2, &booted_kernel[1], &len, NULL, 0);
|
|
|
|
booted_kernel[sizeof(booted_kernel) - 1] = '\0';
|
|
|
|
uf = (booted_kernel[1] == '/') ?
|
|
|
|
&booted_kernel[1] : &booted_kernel[0];
|
|
|
|
if (rc != -1)
|
|
|
|
rc = stat(uf, &st);
|
|
|
|
if (rc != -1 && !S_ISREG(st.st_mode))
|
|
|
|
rc = -1;
|
|
|
|
if (rc == -1)
|
|
|
|
#endif /* CPU_BOOTED_KERNEL */
|
|
|
|
uf = _PATH_UNIX;
|
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
else if (strlen(uf) >= MAXPATHLEN) {
|
|
|
|
_kvm_err(kd, kd->program, "exec file name too long");
|
|
|
|
goto failed;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
if (flag & ~O_RDWR) {
|
|
|
|
_kvm_err(kd, kd->program, "bad flags arg");
|
|
|
|
goto failed;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
if (mf == 0)
|
|
|
|
mf = _PATH_MEM;
|
|
|
|
if (sf == 0)
|
|
|
|
sf = _PATH_DRUM;
|
|
|
|
|
2009-09-14 23:29:20 +04:00
|
|
|
/*
|
|
|
|
* Open the kernel namelist. If /dev/ksyms doesn't
|
|
|
|
* exist, open the current kernel.
|
|
|
|
*/
|
|
|
|
if (ufgiven == 0)
|
2011-10-16 01:08:53 +04:00
|
|
|
kd->nlfd = open(_PATH_KSYMS, O_RDONLY | O_CLOEXEC, 0);
|
2009-09-14 23:29:20 +04:00
|
|
|
if (kd->nlfd < 0) {
|
2011-10-16 01:08:53 +04:00
|
|
|
if ((kd->nlfd = open(uf, O_RDONLY | O_CLOEXEC, 0)) < 0) {
|
2009-09-14 23:29:20 +04:00
|
|
|
_kvm_syserr(kd, kd->program, "%s", uf);
|
|
|
|
goto failed;
|
|
|
|
}
|
2011-09-13 01:11:31 +04:00
|
|
|
strlcpy(kd->kernelname, uf, sizeof(kd->kernelname));
|
2009-09-14 23:29:20 +04:00
|
|
|
} else {
|
2011-09-13 01:11:31 +04:00
|
|
|
strlcpy(kd->kernelname, _PATH_KSYMS, sizeof(kd->kernelname));
|
2009-09-14 23:29:20 +04:00
|
|
|
/*
|
|
|
|
* We're here because /dev/ksyms was opened
|
|
|
|
* successfully. However, we don't want to keep it
|
|
|
|
* open, so we close it now. Later, we will open
|
|
|
|
* it again, since it will be the only case where
|
|
|
|
* kd->nlfd is negative.
|
|
|
|
*/
|
|
|
|
close(kd->nlfd);
|
|
|
|
kd->nlfd = -1;
|
|
|
|
}
|
|
|
|
|
2011-10-16 01:08:53 +04:00
|
|
|
if ((kd->pmfd = open(mf, flag | O_CLOEXEC, 0)) < 0) {
|
1994-05-09 07:17:28 +04:00
|
|
|
_kvm_syserr(kd, kd->program, "%s", mf);
|
|
|
|
goto failed;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
if (fstat(kd->pmfd, &st) < 0) {
|
|
|
|
_kvm_syserr(kd, kd->program, "%s", mf);
|
|
|
|
goto failed;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
2008-01-15 16:57:41 +03:00
|
|
|
if (S_ISCHR(st.st_mode) && strcmp(mf, _PATH_MEM) == 0) {
|
1993-03-21 12:45:37 +03:00
|
|
|
/*
|
2008-01-15 16:57:41 +03:00
|
|
|
* If this is /dev/mem, open kmem too. (Maybe we should
|
1994-05-09 07:17:28 +04:00
|
|
|
* make it work for either /dev/mem or /dev/kmem -- in either
|
|
|
|
* case you're working with a live kernel.)
|
1993-03-21 12:45:37 +03:00
|
|
|
*/
|
2011-10-16 01:08:53 +04:00
|
|
|
if ((kd->vmfd = open(_PATH_KMEM, flag | O_CLOEXEC, 0)) < 0) {
|
1994-05-09 07:17:28 +04:00
|
|
|
_kvm_syserr(kd, kd->program, "%s", _PATH_KMEM);
|
|
|
|
goto failed;
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
2000-05-26 06:42:21 +04:00
|
|
|
kd->alive = KVM_ALIVE_FILES;
|
2011-10-16 01:08:53 +04:00
|
|
|
if ((kd->swfd = open(sf, flag | O_CLOEXEC, 0)) < 0) {
|
2005-07-30 20:32:29 +04:00
|
|
|
if (errno != ENXIO) {
|
|
|
|
_kvm_syserr(kd, kd->program, "%s", sf);
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
/* swap is not configured? not fatal */
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
} else {
|
2008-01-15 16:57:41 +03:00
|
|
|
kd->fdalign = DEV_BSIZE; /* XXX */
|
1993-04-10 19:02:41 +04:00
|
|
|
/*
|
1994-05-09 07:17:28 +04:00
|
|
|
* This is a crash dump.
|
2009-09-14 23:29:20 +04:00
|
|
|
* Initialize the virtual address translation machinery.
|
|
|
|
*
|
1996-03-16 13:23:29 +03:00
|
|
|
* If there is no valid core header, fail silently here.
|
1999-07-02 19:28:49 +04:00
|
|
|
* The address translations however will fail without
|
1996-03-16 13:23:29 +03:00
|
|
|
* header. Things can be made to run by calling
|
|
|
|
* kvm_dump_mkheader() before doing any translation.
|
|
|
|
*/
|
|
|
|
if (_kvm_get_header(kd) == 0) {
|
|
|
|
if (_kvm_initvtop(kd) < 0)
|
|
|
|
goto failed;
|
|
|
|
}
|
1993-04-10 19:02:41 +04:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
return (kd);
|
|
|
|
failed:
|
1993-03-21 12:45:37 +03:00
|
|
|
/*
|
1994-05-09 07:17:28 +04:00
|
|
|
* Copy out the error if doing sane error semantics.
|
1993-03-21 12:45:37 +03:00
|
|
|
*/
|
1994-05-09 07:17:28 +04:00
|
|
|
if (errout != 0)
|
2002-11-17 02:34:30 +03:00
|
|
|
(void)strlcpy(errout, kd->errbuf, _POSIX2_LINE_MAX);
|
1994-05-09 07:17:28 +04:00
|
|
|
(void)kvm_close(kd);
|
1993-03-21 12:45:37 +03:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1996-05-05 08:31:59 +04:00
|
|
|
/*
|
|
|
|
* The kernel dump file (from savecore) contains:
|
|
|
|
* kcore_hdr_t kcore_hdr;
|
|
|
|
* kcore_seg_t cpu_hdr;
|
|
|
|
* (opaque) cpu_data; (size is cpu_hdr.c_size)
|
|
|
|
* kcore_seg_t mem_hdr;
|
|
|
|
* (memory) mem_data; (size is mem_hdr.c_size)
|
1999-07-02 19:28:49 +04:00
|
|
|
*
|
1996-05-05 08:31:59 +04:00
|
|
|
* Note: khdr is padded to khdr.c_hdrsize;
|
|
|
|
* cpu_hdr and mem_hdr are padded to khdr.c_seghdrsize
|
|
|
|
*/
|
1996-03-16 13:23:29 +03:00
|
|
|
static int
|
2007-11-08 20:32:30 +03:00
|
|
|
_kvm_get_header(kvm_t *kd)
|
1996-03-16 13:23:29 +03:00
|
|
|
{
|
1996-05-05 08:31:59 +04:00
|
|
|
kcore_hdr_t kcore_hdr;
|
|
|
|
kcore_seg_t cpu_hdr;
|
|
|
|
kcore_seg_t mem_hdr;
|
|
|
|
size_t offset;
|
|
|
|
ssize_t sz;
|
1996-03-16 13:23:29 +03:00
|
|
|
|
1996-05-05 08:31:59 +04:00
|
|
|
/*
|
|
|
|
* Read the kcore_hdr_t
|
|
|
|
*/
|
1998-07-01 00:40:44 +04:00
|
|
|
sz = Pread(kd, kd->pmfd, &kcore_hdr, sizeof(kcore_hdr), (off_t)0);
|
1996-05-05 08:31:59 +04:00
|
|
|
if (sz != sizeof(kcore_hdr))
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Currently, we only support dump-files made by the current
|
|
|
|
* architecture...
|
|
|
|
*/
|
1996-05-05 08:31:59 +04:00
|
|
|
if ((CORE_GETMAGIC(kcore_hdr) != KCORE_MAGIC) ||
|
|
|
|
(CORE_GETMID(kcore_hdr) != MID_MACHINE))
|
|
|
|
return (-1);
|
1996-03-16 13:23:29 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Currently, we only support exactly 2 segments: cpu-segment
|
|
|
|
* and data-segment in exactly that order.
|
|
|
|
*/
|
1996-05-05 08:31:59 +04:00
|
|
|
if (kcore_hdr.c_nseg != 2)
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
|
|
|
|
/*
|
1996-05-05 08:31:59 +04:00
|
|
|
* Save away the kcore_hdr. All errors after this
|
|
|
|
* should do a to "goto fail" to deallocate things.
|
|
|
|
*/
|
|
|
|
kd->kcore_hdr = _kvm_malloc(kd, sizeof(kcore_hdr));
|
|
|
|
memcpy(kd->kcore_hdr, &kcore_hdr, sizeof(kcore_hdr));
|
|
|
|
offset = kcore_hdr.c_hdrsize;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the CPU segment header
|
1996-03-16 13:23:29 +03:00
|
|
|
*/
|
1998-07-01 00:40:44 +04:00
|
|
|
sz = Pread(kd, kd->pmfd, &cpu_hdr, sizeof(cpu_hdr), (off_t)offset);
|
1996-05-05 08:31:59 +04:00
|
|
|
if (sz != sizeof(cpu_hdr))
|
|
|
|
goto fail;
|
|
|
|
if ((CORE_GETMAGIC(cpu_hdr) != KCORESEG_MAGIC) ||
|
|
|
|
(CORE_GETFLAG(cpu_hdr) != CORE_CPU))
|
|
|
|
goto fail;
|
|
|
|
offset += kcore_hdr.c_seghdrsize;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the CPU segment DATA.
|
|
|
|
*/
|
|
|
|
kd->cpu_dsize = cpu_hdr.c_size;
|
|
|
|
kd->cpu_data = _kvm_malloc(kd, cpu_hdr.c_size);
|
|
|
|
if (kd->cpu_data == NULL)
|
|
|
|
goto fail;
|
1998-07-01 00:40:44 +04:00
|
|
|
sz = Pread(kd, kd->pmfd, kd->cpu_data, cpu_hdr.c_size, (off_t)offset);
|
1996-05-05 08:31:59 +04:00
|
|
|
if (sz != cpu_hdr.c_size)
|
|
|
|
goto fail;
|
|
|
|
offset += cpu_hdr.c_size;
|
1996-03-16 13:23:29 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the next segment header: data segment
|
|
|
|
*/
|
1998-07-01 00:40:44 +04:00
|
|
|
sz = Pread(kd, kd->pmfd, &mem_hdr, sizeof(mem_hdr), (off_t)offset);
|
1996-05-05 08:31:59 +04:00
|
|
|
if (sz != sizeof(mem_hdr))
|
|
|
|
goto fail;
|
|
|
|
offset += kcore_hdr.c_seghdrsize;
|
1996-03-16 13:23:29 +03:00
|
|
|
|
1996-05-05 08:31:59 +04:00
|
|
|
if ((CORE_GETMAGIC(mem_hdr) != KCORESEG_MAGIC) ||
|
|
|
|
(CORE_GETFLAG(mem_hdr) != CORE_DATA))
|
|
|
|
goto fail;
|
1996-03-16 13:23:29 +03:00
|
|
|
|
1996-05-05 08:31:59 +04:00
|
|
|
kd->dump_off = offset;
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (kd->kcore_hdr != NULL) {
|
|
|
|
free(kd->kcore_hdr);
|
1996-03-16 13:23:29 +03:00
|
|
|
kd->kcore_hdr = NULL;
|
1996-05-05 08:31:59 +04:00
|
|
|
}
|
|
|
|
if (kd->cpu_data != NULL) {
|
|
|
|
free(kd->cpu_data);
|
|
|
|
kd->cpu_data = NULL;
|
|
|
|
kd->cpu_dsize = 0;
|
1996-03-16 13:23:29 +03:00
|
|
|
}
|
1997-10-10 12:45:29 +04:00
|
|
|
return (-1);
|
1996-03-16 13:23:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1996-05-05 08:31:59 +04:00
|
|
|
* The format while on the dump device is: (new format)
|
1996-10-04 07:34:09 +04:00
|
|
|
* kcore_seg_t cpu_hdr;
|
|
|
|
* (opaque) cpu_data; (size is cpu_hdr.c_size)
|
|
|
|
* kcore_seg_t mem_hdr;
|
|
|
|
* (memory) mem_data; (size is mem_hdr.c_size)
|
1996-03-16 13:23:29 +03:00
|
|
|
*/
|
|
|
|
int
|
2007-11-08 20:32:30 +03:00
|
|
|
kvm_dump_mkheader(kvm_t *kd, off_t dump_off)
|
1996-03-16 13:23:29 +03:00
|
|
|
{
|
1996-05-05 08:31:59 +04:00
|
|
|
kcore_seg_t cpu_hdr;
|
1998-08-02 01:29:41 +04:00
|
|
|
size_t hdr_size;
|
|
|
|
ssize_t sz;
|
1996-03-16 13:23:29 +03:00
|
|
|
|
1996-03-19 00:14:44 +03:00
|
|
|
if (kd->kcore_hdr != NULL) {
|
|
|
|
_kvm_err(kd, kd->program, "already has a dump header");
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
}
|
1996-03-19 00:14:44 +03:00
|
|
|
if (ISALIVE(kd)) {
|
|
|
|
_kvm_err(kd, kd->program, "don't use on live kernel");
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1996-05-05 08:31:59 +04:00
|
|
|
* Validate new format crash dump
|
1996-03-16 13:23:29 +03:00
|
|
|
*/
|
1998-07-01 00:40:44 +04:00
|
|
|
sz = Pread(kd, kd->pmfd, &cpu_hdr, sizeof(cpu_hdr), dump_off);
|
2012-08-27 03:09:42 +04:00
|
|
|
if (sz != sizeof(cpu_hdr)) {
|
|
|
|
_kvm_err(kd, 0, "read %zx bytes at offset %"PRIx64
|
|
|
|
" for cpu_hdr instead of requested %zu",
|
|
|
|
sz, dump_off, sizeof(cpu_hdr));
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
2012-08-27 03:09:42 +04:00
|
|
|
}
|
1996-06-23 17:56:54 +04:00
|
|
|
if ((CORE_GETMAGIC(cpu_hdr) != KCORE_MAGIC)
|
|
|
|
|| (CORE_GETMID(cpu_hdr) != MID_MACHINE)) {
|
|
|
|
_kvm_err(kd, 0, "invalid magic in cpu_hdr");
|
1996-06-24 00:28:05 +04:00
|
|
|
return (0);
|
1996-06-23 17:56:54 +04:00
|
|
|
}
|
1996-05-05 08:31:59 +04:00
|
|
|
hdr_size = ALIGN(sizeof(cpu_hdr));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the CPU segment.
|
|
|
|
*/
|
|
|
|
kd->cpu_dsize = cpu_hdr.c_size;
|
|
|
|
kd->cpu_data = _kvm_malloc(kd, kd->cpu_dsize);
|
2012-08-27 03:09:42 +04:00
|
|
|
if (kd->cpu_data == NULL) {
|
|
|
|
_kvm_err(kd, kd->program, "no cpu_data");
|
1996-05-05 08:31:59 +04:00
|
|
|
goto fail;
|
2012-08-27 03:09:42 +04:00
|
|
|
}
|
1998-07-01 00:40:44 +04:00
|
|
|
sz = Pread(kd, kd->pmfd, kd->cpu_data, cpu_hdr.c_size,
|
|
|
|
dump_off + hdr_size);
|
2012-08-27 03:09:42 +04:00
|
|
|
if (sz != cpu_hdr.c_size) {
|
|
|
|
_kvm_err(kd, kd->program, "size %zu != cpu_hdr.csize %"PRIu32,
|
|
|
|
sz, cpu_hdr.c_size);
|
1996-05-05 08:31:59 +04:00
|
|
|
goto fail;
|
2012-08-27 03:09:42 +04:00
|
|
|
}
|
1996-05-05 08:31:59 +04:00
|
|
|
hdr_size += kd->cpu_dsize;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Leave phys mem pointer at beginning of memory data
|
|
|
|
*/
|
|
|
|
kd->dump_off = dump_off + hdr_size;
|
2012-08-27 03:09:42 +04:00
|
|
|
if (Lseek(kd, kd->pmfd, kd->dump_off, SEEK_SET) == -1) {
|
|
|
|
_kvm_err(kd, kd->program, "failed to seek to %" PRId64,
|
|
|
|
(int64_t)kd->dump_off);
|
1996-05-05 08:31:59 +04:00
|
|
|
goto fail;
|
2012-08-27 03:09:42 +04:00
|
|
|
}
|
1996-03-16 13:23:29 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a kcore_hdr.
|
|
|
|
*/
|
1996-05-05 08:31:59 +04:00
|
|
|
kd->kcore_hdr = _kvm_malloc(kd, sizeof(kcore_hdr_t));
|
2012-08-27 03:09:42 +04:00
|
|
|
if (kd->kcore_hdr == NULL) {
|
|
|
|
_kvm_err(kd, kd->program, "failed to allocate header");
|
1996-05-05 08:31:59 +04:00
|
|
|
goto fail;
|
2012-08-27 03:09:42 +04:00
|
|
|
}
|
1996-03-16 13:23:29 +03:00
|
|
|
|
1996-03-19 00:14:44 +03:00
|
|
|
kd->kcore_hdr->c_hdrsize = ALIGN(sizeof(kcore_hdr_t));
|
|
|
|
kd->kcore_hdr->c_seghdrsize = ALIGN(sizeof(kcore_seg_t));
|
|
|
|
kd->kcore_hdr->c_nseg = 2;
|
|
|
|
CORE_SETMAGIC(*(kd->kcore_hdr), KCORE_MAGIC, MID_MACHINE,0);
|
1996-03-16 13:23:29 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that we have a valid header, enable translations.
|
|
|
|
*/
|
1996-11-10 02:44:53 +03:00
|
|
|
if (_kvm_initvtop(kd) == 0)
|
|
|
|
/* Success */
|
|
|
|
return (hdr_size);
|
1996-05-05 08:31:59 +04:00
|
|
|
|
|
|
|
fail:
|
|
|
|
if (kd->kcore_hdr != NULL) {
|
|
|
|
free(kd->kcore_hdr);
|
|
|
|
kd->kcore_hdr = NULL;
|
|
|
|
}
|
|
|
|
if (kd->cpu_data != NULL) {
|
|
|
|
free(kd->cpu_data);
|
|
|
|
kd->cpu_data = NULL;
|
|
|
|
kd->cpu_dsize = 0;
|
|
|
|
}
|
|
|
|
return (-1);
|
1996-03-16 13:23:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-11-08 23:48:04 +03:00
|
|
|
clear_gap(kvm_t *kd, bool (*write_buf)(void *, const void *, size_t),
|
|
|
|
void *cookie, size_t size)
|
1996-03-16 13:23:29 +03:00
|
|
|
{
|
2007-11-08 23:48:04 +03:00
|
|
|
char buf[1024];
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
(void)memset(buf, 0, size > sizeof(buf) ? sizeof(buf) : size);
|
|
|
|
|
|
|
|
while (size > 0) {
|
|
|
|
len = size > sizeof(buf) ? sizeof(buf) : size;
|
|
|
|
if (!(*write_buf)(cookie, buf, len)) {
|
1996-03-16 13:23:29 +03:00
|
|
|
_kvm_syserr(kd, kd->program, "clear_gap");
|
2007-11-08 23:48:04 +03:00
|
|
|
return -1;
|
1996-03-16 13:23:29 +03:00
|
|
|
}
|
2007-11-08 23:48:04 +03:00
|
|
|
size -= len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
1996-03-16 13:23:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-11-08 23:48:04 +03:00
|
|
|
* Write the dump header by calling write_buf with cookie as first argument.
|
1996-03-16 13:23:29 +03:00
|
|
|
*/
|
|
|
|
int
|
2007-11-08 23:48:04 +03:00
|
|
|
kvm_dump_header(kvm_t *kd, bool (*write_buf)(void *, const void *, size_t),
|
|
|
|
void *cookie, int dumpsize)
|
1996-03-16 13:23:29 +03:00
|
|
|
{
|
|
|
|
kcore_seg_t seghdr;
|
|
|
|
long offset;
|
2007-11-08 23:48:04 +03:00
|
|
|
size_t gap;
|
1996-03-16 13:23:29 +03:00
|
|
|
|
1996-05-05 08:31:59 +04:00
|
|
|
if (kd->kcore_hdr == NULL || kd->cpu_data == NULL) {
|
1996-03-16 13:23:29 +03:00
|
|
|
_kvm_err(kd, kd->program, "no valid dump header(s)");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the generic header
|
|
|
|
*/
|
|
|
|
offset = 0;
|
2007-11-08 23:48:04 +03:00
|
|
|
if (!(*write_buf)(cookie, kd->kcore_hdr, sizeof(kcore_hdr_t))) {
|
|
|
|
_kvm_syserr(kd, kd->program, "kvm_dump_header");
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
offset += kd->kcore_hdr->c_hdrsize;
|
|
|
|
gap = kd->kcore_hdr->c_hdrsize - sizeof(kcore_hdr_t);
|
2007-11-08 23:48:04 +03:00
|
|
|
if (clear_gap(kd, write_buf, cookie, gap) == -1)
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
|
|
|
|
/*
|
2004-02-13 14:36:08 +03:00
|
|
|
* Write the CPU header
|
1996-03-16 13:23:29 +03:00
|
|
|
*/
|
|
|
|
CORE_SETMAGIC(seghdr, KCORESEG_MAGIC, 0, CORE_CPU);
|
1996-05-05 08:31:59 +04:00
|
|
|
seghdr.c_size = ALIGN(kd->cpu_dsize);
|
2007-11-08 23:48:04 +03:00
|
|
|
if (!(*write_buf)(cookie, &seghdr, sizeof(seghdr))) {
|
|
|
|
_kvm_syserr(kd, kd->program, "kvm_dump_header");
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
offset += kd->kcore_hdr->c_seghdrsize;
|
|
|
|
gap = kd->kcore_hdr->c_seghdrsize - sizeof(seghdr);
|
2007-11-08 23:48:04 +03:00
|
|
|
if (clear_gap(kd, write_buf, cookie, gap) == -1)
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
|
2007-11-08 23:48:04 +03:00
|
|
|
if (!(*write_buf)(cookie, kd->cpu_data, kd->cpu_dsize)) {
|
|
|
|
_kvm_syserr(kd, kd->program, "kvm_dump_header");
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
offset += seghdr.c_size;
|
1996-05-05 08:31:59 +04:00
|
|
|
gap = seghdr.c_size - kd->cpu_dsize;
|
2007-11-09 00:58:26 +03:00
|
|
|
if (clear_gap(kd, write_buf, cookie, gap) == -1)
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the actual dump data segment header
|
|
|
|
*/
|
|
|
|
CORE_SETMAGIC(seghdr, KCORESEG_MAGIC, 0, CORE_DATA);
|
|
|
|
seghdr.c_size = dumpsize;
|
2007-11-08 23:48:04 +03:00
|
|
|
if (!(*write_buf)(cookie, &seghdr, sizeof(seghdr))) {
|
|
|
|
_kvm_syserr(kd, kd->program, "kvm_dump_header");
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
offset += kd->kcore_hdr->c_seghdrsize;
|
|
|
|
gap = kd->kcore_hdr->c_seghdrsize - sizeof(seghdr);
|
2007-11-08 23:48:04 +03:00
|
|
|
if (clear_gap(kd, write_buf, cookie, gap) == -1)
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
|
1998-09-27 22:15:58 +04:00
|
|
|
return (int)offset;
|
1996-03-16 13:23:29 +03:00
|
|
|
}
|
|
|
|
|
2007-11-08 23:48:04 +03:00
|
|
|
static bool
|
|
|
|
kvm_dump_header_stdio(void *cookie, const void *buf, size_t len)
|
|
|
|
{
|
|
|
|
return fwrite(buf, len, 1, (FILE *)cookie) == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
kvm_dump_wrtheader(kvm_t *kd, FILE *fp, int dumpsize)
|
|
|
|
{
|
|
|
|
return kvm_dump_header(kd, kvm_dump_header_stdio, fp, dumpsize);
|
|
|
|
}
|
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
kvm_t *
|
2007-11-08 20:32:30 +03:00
|
|
|
kvm_openfiles(const char *uf, const char *mf, const char *sf,
|
|
|
|
int flag, char *errout)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
1998-02-03 22:12:13 +03:00
|
|
|
kvm_t *kd;
|
1994-02-14 16:46:01 +03:00
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
if ((kd = malloc(sizeof(*kd))) == NULL) {
|
2002-11-17 02:34:30 +03:00
|
|
|
(void)strlcpy(errout, strerror(errno), _POSIX2_LINE_MAX);
|
1994-05-09 07:17:28 +04:00
|
|
|
return (0);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
kd->program = 0;
|
|
|
|
return (_kvm_open(kd, uf, mf, sf, flag, errout));
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
kvm_t *
|
2007-11-08 20:32:30 +03:00
|
|
|
kvm_open(const char *uf, const char *mf, const char *sf, int flag,
|
|
|
|
const char *program)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
1998-02-03 22:12:13 +03:00
|
|
|
kvm_t *kd;
|
1993-03-21 12:45:37 +03:00
|
|
|
|
2006-03-22 02:47:51 +03:00
|
|
|
if ((kd = malloc(sizeof(*kd))) == NULL) {
|
|
|
|
(void)fprintf(stderr, "%s: %s\n",
|
|
|
|
program ? program : getprogname(), strerror(errno));
|
1994-05-09 07:17:28 +04:00
|
|
|
return (0);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
kd->program = program;
|
|
|
|
return (_kvm_open(kd, uf, mf, sf, flag, NULL));
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
int
|
2007-11-08 20:32:30 +03:00
|
|
|
kvm_close(kvm_t *kd)
|
1993-03-21 12:45:37 +03:00
|
|
|
{
|
1998-02-03 22:12:13 +03:00
|
|
|
int error = 0;
|
1994-05-09 07:17:28 +04:00
|
|
|
|
|
|
|
if (kd->pmfd >= 0)
|
|
|
|
error |= close(kd->pmfd);
|
|
|
|
if (kd->vmfd >= 0)
|
|
|
|
error |= close(kd->vmfd);
|
|
|
|
if (kd->nlfd >= 0)
|
|
|
|
error |= close(kd->nlfd);
|
|
|
|
if (kd->swfd >= 0)
|
|
|
|
error |= close(kd->swfd);
|
|
|
|
if (kd->vmst)
|
|
|
|
_kvm_freevtop(kd);
|
1996-05-05 08:31:59 +04:00
|
|
|
kd->cpu_dsize = 0;
|
|
|
|
if (kd->cpu_data != NULL)
|
2008-01-15 16:57:41 +03:00
|
|
|
free(kd->cpu_data);
|
1996-03-16 13:23:29 +03:00
|
|
|
if (kd->kcore_hdr != NULL)
|
2008-01-15 16:57:41 +03:00
|
|
|
free(kd->kcore_hdr);
|
1994-05-09 07:17:28 +04:00
|
|
|
if (kd->procbase != 0)
|
2008-01-15 16:57:41 +03:00
|
|
|
free(kd->procbase);
|
2000-05-26 06:42:21 +04:00
|
|
|
if (kd->procbase2 != 0)
|
2008-01-15 16:57:41 +03:00
|
|
|
free(kd->procbase2);
|
2003-01-18 13:32:11 +03:00
|
|
|
if (kd->lwpbase != 0)
|
2008-01-15 16:57:41 +03:00
|
|
|
free(kd->lwpbase);
|
1994-09-18 05:52:01 +04:00
|
|
|
if (kd->swapspc != 0)
|
2008-01-15 16:57:41 +03:00
|
|
|
free(kd->swapspc);
|
1994-09-18 05:52:01 +04:00
|
|
|
if (kd->argspc != 0)
|
2008-01-15 16:57:41 +03:00
|
|
|
free(kd->argspc);
|
1995-05-16 18:21:08 +04:00
|
|
|
if (kd->argbuf != 0)
|
2008-01-15 16:57:41 +03:00
|
|
|
free(kd->argbuf);
|
1994-05-09 07:17:28 +04:00
|
|
|
if (kd->argv != 0)
|
2008-01-15 16:57:41 +03:00
|
|
|
free(kd->argv);
|
|
|
|
if (kd->iobuf != 0)
|
|
|
|
free(kd->iobuf);
|
|
|
|
free(kd);
|
1993-03-21 12:45:37 +03:00
|
|
|
|
2010-11-27 01:01:53 +03:00
|
|
|
return (error);
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|
|
|
|
|
1993-08-16 11:27:06 +04:00
|
|
|
int
|
2007-11-08 20:32:30 +03:00
|
|
|
kvm_nlist(kvm_t *kd, struct nlist *nl)
|
1993-08-16 11:27:06 +04:00
|
|
|
{
|
2003-11-04 17:59:10 +03:00
|
|
|
int rv, nlfd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* kd->nlfd might be negative when we get here, and in that
|
|
|
|
* case that means that we're using /dev/ksyms.
|
|
|
|
* So open it again, just for the time we retrieve the list.
|
|
|
|
*/
|
|
|
|
if (kd->nlfd < 0) {
|
2011-10-16 01:08:53 +04:00
|
|
|
nlfd = open(_PATH_KSYMS, O_RDONLY | O_CLOEXEC, 0);
|
2003-11-04 17:59:10 +03:00
|
|
|
if (nlfd < 0) {
|
|
|
|
_kvm_err(kd, 0, "failed to open %s", _PATH_KSYMS);
|
|
|
|
return (nlfd);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
nlfd = kd->nlfd;
|
1996-03-16 13:23:29 +03:00
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
/*
|
2003-05-11 17:37:34 +04:00
|
|
|
* Call the nlist(3) routines to retrieve the given namelist.
|
1994-05-09 07:17:28 +04:00
|
|
|
*/
|
2003-11-04 17:59:10 +03:00
|
|
|
rv = __fdnlist(nlfd, nl);
|
|
|
|
|
2003-05-11 17:37:34 +04:00
|
|
|
if (rv == -1)
|
|
|
|
_kvm_err(kd, 0, "bad namelist");
|
2003-11-04 17:59:10 +03:00
|
|
|
|
|
|
|
if (kd->nlfd < 0)
|
|
|
|
close(nlfd);
|
|
|
|
|
2003-05-11 17:37:34 +04:00
|
|
|
return (rv);
|
1993-08-16 11:27:06 +04:00
|
|
|
}
|
1993-04-10 19:02:41 +04:00
|
|
|
|
2007-11-08 20:32:30 +03:00
|
|
|
int
|
|
|
|
kvm_dump_inval(kvm_t *kd)
|
1996-03-16 13:23:29 +03:00
|
|
|
{
|
1998-08-02 01:29:41 +04:00
|
|
|
struct nlist nl[2];
|
Change kvm_pa2off() and kvm_kvatop() prototypes (private to kvm(3)):
-int _kvm_kvatop(kvm_t *, u_long, u_long *);
-off_t _kvm_pa2off(kvm_t *, u_long);
+int _kvm_kvatop(kvm_t *, vaddr_t, paddr_t *);
+off_t _kvm_pa2off(kvm_t *, paddr_t);
Basically, use vaddr_t for VA and paddr_t for PA. In addition, for variables
representing addresses, use paddr_t or vaddr_t, depending on the context.
For most arches, vaddr_t and paddr_t are equivalent to unsigned long. However,
the change was needed for exotic situations, like i386 PAE, were unsigned long
is not suitable for PA which are 64 bits long. As this required a complete
change of the function prototypes, all arches had to be adapted accordingly.
Core files from before this commit should still work with the new code; I did
not see any direct dependency between core's structure and kvatop/pa2off.
The change was compile tested for all arches, as it impacts all of them.
See also:
http://mail-index.netbsd.org/current-users/2010/09/07/msg014249.html
2010-09-21 03:23:16 +04:00
|
|
|
paddr_t pa;
|
2008-01-15 17:16:30 +03:00
|
|
|
size_t dsize;
|
|
|
|
off_t doff;
|
|
|
|
void *newbuf;
|
1996-03-16 13:23:29 +03:00
|
|
|
|
|
|
|
if (ISALIVE(kd)) {
|
|
|
|
_kvm_err(kd, kd->program, "clearing dump on live kernel");
|
|
|
|
return (-1);
|
|
|
|
}
|
1998-08-02 01:29:41 +04:00
|
|
|
nl[0].n_name = "_dumpmag";
|
|
|
|
nl[1].n_name = NULL;
|
1996-03-16 13:23:29 +03:00
|
|
|
|
1998-08-02 01:29:41 +04:00
|
|
|
if (kvm_nlist(kd, nl) == -1) {
|
1996-03-16 13:23:29 +03:00
|
|
|
_kvm_err(kd, 0, "bad namelist");
|
|
|
|
return (-1);
|
|
|
|
}
|
Change kvm_pa2off() and kvm_kvatop() prototypes (private to kvm(3)):
-int _kvm_kvatop(kvm_t *, u_long, u_long *);
-off_t _kvm_pa2off(kvm_t *, u_long);
+int _kvm_kvatop(kvm_t *, vaddr_t, paddr_t *);
+off_t _kvm_pa2off(kvm_t *, paddr_t);
Basically, use vaddr_t for VA and paddr_t for PA. In addition, for variables
representing addresses, use paddr_t or vaddr_t, depending on the context.
For most arches, vaddr_t and paddr_t are equivalent to unsigned long. However,
the change was needed for exotic situations, like i386 PAE, were unsigned long
is not suitable for PA which are 64 bits long. As this required a complete
change of the function prototypes, all arches had to be adapted accordingly.
Core files from before this commit should still work with the new code; I did
not see any direct dependency between core's structure and kvatop/pa2off.
The change was compile tested for all arches, as it impacts all of them.
See also:
http://mail-index.netbsd.org/current-users/2010/09/07/msg014249.html
2010-09-21 03:23:16 +04:00
|
|
|
if (_kvm_kvatop(kd, (vaddr_t)nl[0].n_value, &pa) == 0)
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
|
|
|
|
errno = 0;
|
2008-01-15 17:16:30 +03:00
|
|
|
dsize = MAX(kd->fdalign, sizeof(u_long));
|
|
|
|
if (kd->iobufsz < dsize) {
|
|
|
|
newbuf = realloc(kd->iobuf, dsize);
|
|
|
|
if (newbuf == NULL) {
|
|
|
|
_kvm_syserr(kd, 0, "cannot allocate I/O buffer");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
kd->iobuf = newbuf;
|
|
|
|
kd->iobufsz = dsize;
|
|
|
|
}
|
|
|
|
memset(kd->iobuf, 0, dsize);
|
|
|
|
doff = _kvm_pa2off(kd, pa);
|
|
|
|
doff -= doff % kd->fdalign;
|
|
|
|
if (pwrite(kd->pmfd, kd->iobuf, dsize, doff) == -1) {
|
1998-07-01 00:29:39 +04:00
|
|
|
_kvm_syserr(kd, 0, "cannot invalidate dump - pwrite");
|
1996-03-16 13:23:29 +03:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
ssize_t
|
2007-11-08 20:32:30 +03:00
|
|
|
kvm_read(kvm_t *kd, u_long kva, void *buf, size_t len)
|
1993-04-10 19:02:41 +04:00
|
|
|
{
|
1998-02-03 22:12:13 +03:00
|
|
|
int cc;
|
|
|
|
void *cp;
|
1993-04-10 19:02:41 +04:00
|
|
|
|
2000-05-26 06:42:21 +04:00
|
|
|
if (ISKMEM(kd)) {
|
1994-05-09 07:17:28 +04:00
|
|
|
/*
|
|
|
|
* We're using /dev/kmem. Just read straight from the
|
|
|
|
* device and let the active kernel do the address translation.
|
|
|
|
*/
|
|
|
|
errno = 0;
|
2008-01-15 16:57:41 +03:00
|
|
|
cc = _kvm_pread(kd, kd->vmfd, buf, len, (off_t)kva);
|
1994-05-09 07:17:28 +04:00
|
|
|
if (cc < 0) {
|
|
|
|
_kvm_syserr(kd, 0, "kvm_read");
|
1998-06-30 00:36:30 +04:00
|
|
|
return (-1);
|
1994-05-09 07:17:28 +04:00
|
|
|
} else if (cc < len)
|
|
|
|
_kvm_err(kd, kd->program, "short read");
|
|
|
|
return (cc);
|
2000-05-26 06:42:21 +04:00
|
|
|
} else if (ISSYSCTL(kd)) {
|
|
|
|
_kvm_err(kd, kd->program, "kvm_open called with KVM_NO_FILES, "
|
|
|
|
"can't use kvm_read");
|
|
|
|
return (-1);
|
1994-05-09 07:17:28 +04:00
|
|
|
} else {
|
1996-05-05 08:31:59 +04:00
|
|
|
if ((kd->kcore_hdr == NULL) || (kd->cpu_data == NULL)) {
|
1996-03-16 13:23:29 +03:00
|
|
|
_kvm_err(kd, kd->program, "no valid dump header");
|
1998-06-30 00:36:30 +04:00
|
|
|
return (-1);
|
1996-03-16 13:23:29 +03:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
cp = buf;
|
|
|
|
while (len > 0) {
|
Change kvm_pa2off() and kvm_kvatop() prototypes (private to kvm(3)):
-int _kvm_kvatop(kvm_t *, u_long, u_long *);
-off_t _kvm_pa2off(kvm_t *, u_long);
+int _kvm_kvatop(kvm_t *, vaddr_t, paddr_t *);
+off_t _kvm_pa2off(kvm_t *, paddr_t);
Basically, use vaddr_t for VA and paddr_t for PA. In addition, for variables
representing addresses, use paddr_t or vaddr_t, depending on the context.
For most arches, vaddr_t and paddr_t are equivalent to unsigned long. However,
the change was needed for exotic situations, like i386 PAE, were unsigned long
is not suitable for PA which are 64 bits long. As this required a complete
change of the function prototypes, all arches had to be adapted accordingly.
Core files from before this commit should still work with the new code; I did
not see any direct dependency between core's structure and kvatop/pa2off.
The change was compile tested for all arches, as it impacts all of them.
See also:
http://mail-index.netbsd.org/current-users/2010/09/07/msg014249.html
2010-09-21 03:23:16 +04:00
|
|
|
paddr_t pa;
|
1996-03-16 13:23:29 +03:00
|
|
|
off_t foff;
|
1999-07-02 19:28:49 +04:00
|
|
|
|
Change kvm_pa2off() and kvm_kvatop() prototypes (private to kvm(3)):
-int _kvm_kvatop(kvm_t *, u_long, u_long *);
-off_t _kvm_pa2off(kvm_t *, u_long);
+int _kvm_kvatop(kvm_t *, vaddr_t, paddr_t *);
+off_t _kvm_pa2off(kvm_t *, paddr_t);
Basically, use vaddr_t for VA and paddr_t for PA. In addition, for variables
representing addresses, use paddr_t or vaddr_t, depending on the context.
For most arches, vaddr_t and paddr_t are equivalent to unsigned long. However,
the change was needed for exotic situations, like i386 PAE, were unsigned long
is not suitable for PA which are 64 bits long. As this required a complete
change of the function prototypes, all arches had to be adapted accordingly.
Core files from before this commit should still work with the new code; I did
not see any direct dependency between core's structure and kvatop/pa2off.
The change was compile tested for all arches, as it impacts all of them.
See also:
http://mail-index.netbsd.org/current-users/2010/09/07/msg014249.html
2010-09-21 03:23:16 +04:00
|
|
|
cc = _kvm_kvatop(kd, (vaddr_t)kva, &pa);
|
1994-05-09 07:17:28 +04:00
|
|
|
if (cc == 0)
|
1998-06-30 00:36:30 +04:00
|
|
|
return (-1);
|
1994-05-09 07:17:28 +04:00
|
|
|
if (cc > len)
|
|
|
|
cc = len;
|
1996-03-16 13:23:29 +03:00
|
|
|
foff = _kvm_pa2off(kd, pa);
|
1994-05-09 07:17:28 +04:00
|
|
|
errno = 0;
|
2008-01-15 16:57:41 +03:00
|
|
|
cc = _kvm_pread(kd, kd->pmfd, cp, (size_t)cc, foff);
|
1994-05-09 07:17:28 +04:00
|
|
|
if (cc < 0) {
|
|
|
|
_kvm_syserr(kd, kd->program, "kvm_read");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If kvm_kvatop returns a bogus value or our core
|
|
|
|
* file is truncated, we might wind up seeking beyond
|
|
|
|
* the end of the core file in which case the read will
|
|
|
|
* return 0 (EOF).
|
|
|
|
*/
|
|
|
|
if (cc == 0)
|
|
|
|
break;
|
1995-07-04 07:03:30 +04:00
|
|
|
cp = (char *)cp + cc;
|
1994-05-09 07:17:28 +04:00
|
|
|
kva += cc;
|
|
|
|
len -= cc;
|
1993-04-10 19:02:41 +04:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
return ((char *)cp - (char *)buf);
|
1993-04-10 19:02:41 +04:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
/* NOTREACHED */
|
1993-04-10 19:02:41 +04:00
|
|
|
}
|
|
|
|
|
1994-05-09 07:17:28 +04:00
|
|
|
ssize_t
|
2007-11-08 20:32:30 +03:00
|
|
|
kvm_write(kvm_t *kd, u_long kva, const void *buf, size_t len)
|
1994-02-14 16:46:01 +03:00
|
|
|
{
|
1998-02-03 22:12:13 +03:00
|
|
|
int cc;
|
1994-02-14 16:46:01 +03:00
|
|
|
|
2000-05-26 06:42:21 +04:00
|
|
|
if (ISKMEM(kd)) {
|
1994-05-09 07:17:28 +04:00
|
|
|
/*
|
|
|
|
* Just like kvm_read, only we write.
|
|
|
|
*/
|
|
|
|
errno = 0;
|
1998-07-01 00:29:39 +04:00
|
|
|
cc = pwrite(kd->vmfd, buf, len, (off_t)kva);
|
1994-05-09 07:17:28 +04:00
|
|
|
if (cc < 0) {
|
|
|
|
_kvm_syserr(kd, 0, "kvm_write");
|
1998-06-30 00:36:30 +04:00
|
|
|
return (-1);
|
1994-05-09 07:17:28 +04:00
|
|
|
} else if (cc < len)
|
|
|
|
_kvm_err(kd, kd->program, "short write");
|
|
|
|
return (cc);
|
2000-05-26 06:42:21 +04:00
|
|
|
} else if (ISSYSCTL(kd)) {
|
|
|
|
_kvm_err(kd, kd->program, "kvm_open called with KVM_NO_FILES, "
|
|
|
|
"can't use kvm_write");
|
|
|
|
return (-1);
|
1994-05-09 07:17:28 +04:00
|
|
|
} else {
|
|
|
|
_kvm_err(kd, kd->program,
|
|
|
|
"kvm_write not implemented for dead kernels");
|
1998-06-30 00:36:30 +04:00
|
|
|
return (-1);
|
1994-02-14 16:46:01 +03:00
|
|
|
}
|
1994-05-09 07:17:28 +04:00
|
|
|
/* NOTREACHED */
|
1993-03-21 12:45:37 +03:00
|
|
|
}
|