teach libkvm about vkernels
authorAggelos Economopoulos <aoiko@cc.ece.ntua.gr>
Mon, 4 May 2009 20:19:19 +0000 (23:19 +0300)
committerAggelos Economopoulos <aoiko@cc.ece.ntua.gr>
Mon, 4 May 2009 20:28:30 +0000 (23:28 +0300)
Add support for accessing a running vkernel's memory by
reading its /proc/$pid/mem file.

lib/libkvm/kvm.c
lib/libkvm/kvm_amd64.c
lib/libkvm/kvm_file.c
lib/libkvm/kvm_getloadavg.c
lib/libkvm/kvm_i386.c
lib/libkvm/kvm_private.h
lib/libkvm/kvm_proc.c
lib/libkvm/kvm_sparc.c

index cec6801..4d1226a 100644 (file)
@@ -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.
                 */
index f6e211d..d832497 100644 (file)
@@ -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);
        }
index e4e9af4..09ef27d 100644 (file)
@@ -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;
index 6dbe563..4a3ea4c 100644 (file)
@@ -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) {
index 2961e18..5345456 100644 (file)
@@ -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);
        }
index 1dd03d2..b25b34a 100644 (file)
@@ -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.
  */
index e7e2681..1c39636 100644 (file)
@@ -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);
index 344cab8..23f0d56 100644 (file)
@@ -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);