From 104d324f4652985759b15ee88db65e94166dea81 Mon Sep 17 00:00:00 2001 From: Aggelos Economopoulos Date: Mon, 4 May 2009 23:19:19 +0300 Subject: [PATCH] teach libkvm about vkernels Add support for accessing a running vkernel's memory by reading its /proc/$pid/mem file. --- lib/libkvm/kvm.c | 61 ++++++++++++++++++++++++++++--------- lib/libkvm/kvm_amd64.c | 2 +- lib/libkvm/kvm_file.c | 2 +- lib/libkvm/kvm_getloadavg.c | 2 +- lib/libkvm/kvm_i386.c | 2 +- lib/libkvm/kvm_private.h | 29 ++++++++++++++++-- lib/libkvm/kvm_proc.c | 8 ++--- lib/libkvm/kvm_sparc.c | 2 +- 8 files changed, 83 insertions(+), 25 deletions(-) diff --git a/lib/libkvm/kvm.c b/lib/libkvm/kvm.c index cec68010b8..4d1226a2f9 100644 --- a/lib/libkvm/kvm.c +++ b/lib/libkvm/kvm.c @@ -132,6 +132,22 @@ _kvm_malloc(kvm_t *kd, size_t n) return (p); } +static int +is_proc_mem(const char *p) +{ + static char proc[] = "/proc/"; + static char mem[] = "/mem"; + if (strncmp(proc, p, sizeof(proc) - 1)) + return 0; + p += sizeof(proc) - 1; + for (; p != '\0'; ++p) + if (!isdigit(*p)) + break; + if (!isdigit(*(p - 1))) + return 0; + return !strncmp(p, mem, sizeof(mem) - 1); +} + static kvm_t * _kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout) { @@ -145,6 +161,7 @@ _kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout) kd->procend = NULL; kd->argspc = 0; kd->argv = 0; + kd->flags = 0; if (uf == 0) uf = getbootfile(); @@ -167,11 +184,6 @@ _kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout) _kvm_syserr(kd, kd->program, "%s", mf); goto failed; } - if (S_ISREG(st.st_mode) && st.st_size <= 0) { - errno = EINVAL; - _kvm_syserr(kd, kd->program, "empty file"); - goto failed; - } if (fcntl(kd->pmfd, F_SETFD, FD_CLOEXEC) < 0) { _kvm_syserr(kd, kd->program, "%s", mf); goto failed; @@ -195,11 +207,11 @@ _kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout) goto failed; } } + kd->flags |= KVMF_HOST; } else { /* - * This is a crash dump. - * Initialize the virtual address translation machinery, - * but first setup the namelist fd. + * Crash dump or vkernel /proc/$pid/mem file: + * can't use kldsym, we are going to need ->nlfd */ if ((kd->nlfd = open(uf, O_RDONLY, 0)) < 0) { _kvm_syserr(kd, kd->program, "%s", uf); @@ -209,8 +221,29 @@ _kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout) _kvm_syserr(kd, kd->program, "%s", uf); goto failed; } - if (_kvm_initvtop(kd) < 0) - goto failed; + if(is_proc_mem(mf)) { + /* + * It's /proc/$pid/mem, so we rely on the host + * kernel to do address translation for us. + */ + kd->vmfd = kd->pmfd; + kd->pmfd = -1; + kd->flags |= KVMF_VKERN; + } else { + if (st.st_size <= 0) { + errno = EINVAL; + _kvm_syserr(kd, kd->program, "empty file"); + goto failed; + } + + /* + * This is a crash dump. + * Initialize the virtual address translation machinery, + * but first setup the namelist fd. + */ + if (_kvm_initvtop(kd) < 0) + goto failed; + } } return (kd); failed: @@ -289,7 +322,7 @@ kvm_nlist(kvm_t *kd, struct nlist *nl) * If we can't use the kld symbol lookup, revert to the * slow library call. */ - if (!ISALIVE(kd)) + if (!kvm_ishost(kd)) return (__fdnlist(kd->nlfd, nl)); /* @@ -331,7 +364,7 @@ kvm_read(kvm_t *kd, u_long kva, void *buf, size_t len) int cc; void *cp; - if (ISALIVE(kd)) { + if (kvm_notrans(kd)) { /* * We're using /dev/kmem. Just read straight from the * device and let the active kernel do the address translation. @@ -413,7 +446,7 @@ kvm_readstr(kvm_t *kd, u_long kva, char *buf, size_t *lenp) len = *lenp; } - if (ISALIVE(kd)) { + if (kvm_notrans(kd)) { /* * We're using /dev/kmem. Just read straight from the * device and let the active kernel do the address translation. @@ -497,7 +530,7 @@ kvm_write(kvm_t *kd, u_long kva, const void *buf, size_t len) { int cc; - if (ISALIVE(kd)) { + if (kvm_notrans(kd)) { /* * Just like kvm_read, only we write. */ diff --git a/lib/libkvm/kvm_amd64.c b/lib/libkvm/kvm_amd64.c index f6e211d52d..d8324972f5 100644 --- a/lib/libkvm/kvm_amd64.c +++ b/lib/libkvm/kvm_amd64.c @@ -138,7 +138,7 @@ _kvm_vatop(kvm_t *kd, u_long va, u_long *pa) u_long pteindex; int i; - if (ISALIVE(kd)) { + if (kvm_ishost(kd)) { _kvm_err(kd, 0, "vatop called in live kernel!"); return((off_t)0); } diff --git a/lib/libkvm/kvm_file.c b/lib/libkvm/kvm_file.c index e4e9af47f6..09ef27ddff 100644 --- a/lib/libkvm/kvm_file.c +++ b/lib/libkvm/kvm_file.c @@ -121,7 +121,7 @@ kvm_getfiles(kvm_t *kd, int op, int arg, int *cnt) struct file *fp, *fplim; struct filelist filehead; - if (ISALIVE(kd)) { + if (kvm_ishost(kd)) { size = 0; mib[0] = CTL_KERN; mib[1] = KERN_FILE; diff --git a/lib/libkvm/kvm_getloadavg.c b/lib/libkvm/kvm_getloadavg.c index 6dbe56365c..4a3ea4c097 100644 --- a/lib/libkvm/kvm_getloadavg.c +++ b/lib/libkvm/kvm_getloadavg.c @@ -70,7 +70,7 @@ kvm_getloadavg(kvm_t *kd, double loadavg[], int nelem) struct nlist *p; int fscale, i; - if (ISALIVE(kd)) + if (kvm_ishost(kd)) return (getloadavg(loadavg, nelem)); if (kvm_nlist(kd, nl) != 0) { diff --git a/lib/libkvm/kvm_i386.c b/lib/libkvm/kvm_i386.c index 2961e1866b..5345456636 100644 --- a/lib/libkvm/kvm_i386.c +++ b/lib/libkvm/kvm_i386.c @@ -138,7 +138,7 @@ _kvm_vatop(kvm_t *kd, u_long va, u_long *pa) u_long pteindex; int i; - if (ISALIVE(kd)) { + if (kvm_ishost(kd)) { _kvm_err(kd, 0, "vatop called in live kernel!"); return((off_t)0); } diff --git a/lib/libkvm/kvm_private.h b/lib/libkvm/kvm_private.h index 1dd03d2e49..b25b34ab0c 100644 --- a/lib/libkvm/kvm_private.h +++ b/lib/libkvm/kvm_private.h @@ -48,10 +48,9 @@ struct __kvm { const char *program; char *errp; /* XXX this can probably go away */ char errbuf[_POSIX2_LINE_MAX]; -#define ISALIVE(kd) ((kd)->vmfd >= 0) int pmfd; /* physical memory file (or crashdump) */ int vmfd; /* virtual memory file (-1 if crashdump) */ - int unused; /* was: swap file (e.g., /dev/drum) */ + int flags; /* was: swap file (e.g., /dev/drum) */ int nlfd; /* namelist file (e.g., /kernel) */ struct kinfo_proc *procbase; struct kinfo_proc *procend; @@ -69,6 +68,32 @@ struct __kvm { struct vmstate *vmst; }; +enum { + KVMF_HOST = 0x1, + KVMF_VKERN = 0x2, +}; + +static inline +int +kvm_ishost(kvm_t *kd) +{ + return kd->flags & KVMF_HOST; +} + +static inline +int +kvm_isvkernel(kvm_t *kd) +{ + return kd->flags & KVMF_VKERN; +} + +static inline +int +kvm_notrans(kvm_t *kd) +{ + return kvm_ishost(kd) || kvm_isvkernel(kd); +} + /* * Functions used internally by kvm, but across kvm modules. */ diff --git a/lib/libkvm/kvm_proc.c b/lib/libkvm/kvm_proc.c index e7e2681eb3..1c39636bb5 100644 --- a/lib/libkvm/kvm_proc.c +++ b/lib/libkvm/kvm_proc.c @@ -468,7 +468,7 @@ kvm_getprocs(kvm_t *kd, int op, int arg, int *cnt) */ kd->procbase = 0; } - if (ISALIVE(kd)) { + if (kvm_ishost(kd)) { size = 0; mib[0] = CTL_KERN; mib[1] = KERN_PROC; @@ -807,7 +807,7 @@ kvm_doargv(kvm_t *kd, const struct kinfo_proc *kp, int nchr, /* * For live kernels, make sure this process didn't go away. */ - if (ap != 0 && ISALIVE(kd) && + if (ap != 0 && (kvm_ishost(kd) || kvm_isvkernel(kd)) && !proc_verify(kd, kp)) ap = 0; return (ap); @@ -827,7 +827,7 @@ kvm_getargv(kvm_t *kd, const struct kinfo_proc *kp, int nchr) static char **bufp; static int argc; - if (!ISALIVE(kd)) { + if (!kvm_ishost(kd)) { /* XXX: vkernels */ _kvm_err(kd, kd->program, "cannot read user space from dead kernel"); return (0); @@ -892,7 +892,7 @@ kvm_uread(kvm_t *kd, pid_t pid, u_long uva, char *buf, size_t len) ssize_t amount; int fd; - if (!ISALIVE(kd)) { + if (!kvm_ishost(kd)) { /* XXX: vkernels */ _kvm_err(kd, kd->program, "cannot read user space from dead kernel"); return (0); diff --git a/lib/libkvm/kvm_sparc.c b/lib/libkvm/kvm_sparc.c index 344cab864d..23f0d567cd 100644 --- a/lib/libkvm/kvm_sparc.c +++ b/lib/libkvm/kvm_sparc.c @@ -183,7 +183,7 @@ _kvm_uvatop(kvm_t *kd, const struct proc *p, u_long va, u_long *pa) * (with holes in the address space), while crashdumps * adhere to the contiguous software model. */ - if (ISALIVE(kd)) + if (kvm_ishost(kd)) frame = pte & PG_PFNUM; else frame = HWTOSW(kd->vmst->pmap_stod, pte & PG_PFNUM); -- 2.41.0