Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libkvm / kvm.c
1 /*-
2  * Copyright (c) 1989, 1992, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software developed by the Computer Systems
6  * Engineering group at Lawrence Berkeley Laboratory under DARPA contract
7  * BG 91-66 and contributed to Berkeley.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by the University of
20  *      California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37
38 #if defined(LIBC_SCCS) && !defined(lint)
39 #if 0
40 static char sccsid[] = "@(#)kvm.c       8.2 (Berkeley) 2/13/94";
41 #else
42 static const char rcsid[] =
43  "$FreeBSD: src/lib/libkvm/kvm.c,v 1.12.2.3 2002/09/13 14:53:43 nectar Exp $";
44 #endif
45 #endif /* LIBC_SCCS and not lint */
46
47 #include <sys/param.h>
48 #include <sys/user.h>
49 #include <sys/proc.h>
50 #include <sys/ioctl.h>
51 #include <sys/stat.h>
52 #include <sys/sysctl.h>
53 #include <sys/linker.h>
54
55 #include <vm/vm.h>
56 #include <vm/vm_param.h>
57 #include <vm/swap_pager.h>
58
59 #include <machine/vmparam.h>
60
61 #include <ctype.h>
62 #include <fcntl.h>
63 #include <kvm.h>
64 #include <limits.h>
65 #include <nlist.h>
66 #include <paths.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <unistd.h>
71
72 #include "kvm_private.h"
73
74 /* from src/lib/libc/gen/nlist.c */
75 int __fdnlist           __P((int, struct nlist *));
76
77 char *
78 kvm_geterr(kd)
79         kvm_t *kd;
80 {
81         return (kd->errbuf);
82 }
83
84 #if __STDC__
85 #include <stdarg.h>
86 #else
87 #include <varargs.h>
88 #endif
89
90 /*
91  * Report an error using printf style arguments.  "program" is kd->program
92  * on hard errors, and 0 on soft errors, so that under sun error emulation,
93  * only hard errors are printed out (otherwise, programs like gdb will
94  * generate tons of error messages when trying to access bogus pointers).
95  */
96 void
97 #if __STDC__
98 _kvm_err(kvm_t *kd, const char *program, const char *fmt, ...)
99 #else
100 _kvm_err(kd, program, fmt, va_alist)
101         kvm_t *kd;
102         char *program, *fmt;
103         va_dcl
104 #endif
105 {
106         va_list ap;
107
108 #ifdef __STDC__
109         va_start(ap, fmt);
110 #else
111         va_start(ap);
112 #endif
113         if (program != NULL) {
114                 (void)fprintf(stderr, "%s: ", program);
115                 (void)vfprintf(stderr, fmt, ap);
116                 (void)fputc('\n', stderr);
117         } else
118                 (void)vsnprintf(kd->errbuf,
119                     sizeof(kd->errbuf), (char *)fmt, ap);
120
121         va_end(ap);
122 }
123
124 void
125 #if __STDC__
126 _kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...)
127 #else
128 _kvm_syserr(kd, program, fmt, va_alist)
129         kvm_t *kd;
130         char *program, *fmt;
131         va_dcl
132 #endif
133 {
134         va_list ap;
135         register int n;
136
137 #if __STDC__
138         va_start(ap, fmt);
139 #else
140         va_start(ap);
141 #endif
142         if (program != NULL) {
143                 (void)fprintf(stderr, "%s: ", program);
144                 (void)vfprintf(stderr, fmt, ap);
145                 (void)fprintf(stderr, ": %s\n", strerror(errno));
146         } else {
147                 register char *cp = kd->errbuf;
148
149                 (void)vsnprintf(cp, sizeof(kd->errbuf), (char *)fmt, ap);
150                 n = strlen(cp);
151                 (void)snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s",
152                     strerror(errno));
153         }
154         va_end(ap);
155 }
156
157 void *
158 _kvm_malloc(kd, n)
159         register kvm_t *kd;
160         register size_t n;
161 {
162         void *p;
163
164         if ((p = calloc(n, sizeof(char))) == NULL)
165                 _kvm_err(kd, kd->program, "can't allocate %u bytes: %s",
166                          n, strerror(errno));
167         return (p);
168 }
169
170 static kvm_t *
171 _kvm_open(kd, uf, mf, flag, errout)
172         register kvm_t *kd;
173         const char *uf;
174         const char *mf;
175         int flag;
176         char *errout;
177 {
178         struct stat st;
179
180         kd->vmfd = -1;
181         kd->pmfd = -1;
182         kd->nlfd = -1;
183         kd->vmst = 0;
184         kd->procbase = 0;
185         kd->argspc = 0;
186         kd->argv = 0;
187
188         if (uf == 0)
189                 uf = getbootfile();
190         else if (strlen(uf) >= MAXPATHLEN) {
191                 _kvm_err(kd, kd->program, "exec file name too long");
192                 goto failed;
193         }
194         if (flag & ~O_RDWR) {
195                 _kvm_err(kd, kd->program, "bad flags arg");
196                 goto failed;
197         }
198         if (mf == 0)
199                 mf = _PATH_MEM;
200
201         if ((kd->pmfd = open(mf, flag, 0)) < 0) {
202                 _kvm_syserr(kd, kd->program, "%s", mf);
203                 goto failed;
204         }
205         if (fstat(kd->pmfd, &st) < 0) {
206                 _kvm_syserr(kd, kd->program, "%s", mf);
207                 goto failed;
208         }
209         if (fcntl(kd->pmfd, F_SETFD, FD_CLOEXEC) < 0) {
210                 _kvm_syserr(kd, kd->program, "%s", mf);
211                 goto failed;
212         }
213         if (S_ISCHR(st.st_mode)) {
214                 /*
215                  * If this is a character special device, then check that
216                  * it's /dev/mem.  If so, open kmem too.  (Maybe we should
217                  * make it work for either /dev/mem or /dev/kmem -- in either
218                  * case you're working with a live kernel.)
219                  */
220                 if (strcmp(mf, _PATH_DEVNULL) == 0) {
221                         kd->vmfd = open(_PATH_DEVNULL, O_RDONLY);
222                 } else if (strcmp(mf, _PATH_MEM) != 0) {
223                         _kvm_err(kd, kd->program,
224                                  "%s: not physical memory device", mf);
225                         goto failed;
226                 } else {
227                         if ((kd->vmfd = open(_PATH_KMEM, flag)) < 0) {
228                                 _kvm_syserr(kd, kd->program, "%s", _PATH_KMEM);
229                                 goto failed;
230                         }
231                         if (fcntl(kd->vmfd, F_SETFD, FD_CLOEXEC) < 0) {
232                                 _kvm_syserr(kd, kd->program, "%s", _PATH_KMEM);
233                                 goto failed;
234                         }
235                 }
236         } else {
237                 /*
238                  * This is a crash dump.
239                  * Initialize the virtual address translation machinery,
240                  * but first setup the namelist fd.
241                  */
242                 if ((kd->nlfd = open(uf, O_RDONLY, 0)) < 0) {
243                         _kvm_syserr(kd, kd->program, "%s", uf);
244                         goto failed;
245                 }
246                 if (fcntl(kd->nlfd, F_SETFD, FD_CLOEXEC) < 0) {
247                         _kvm_syserr(kd, kd->program, "%s", uf);
248                         goto failed;
249                 }
250                 if (_kvm_initvtop(kd) < 0)
251                         goto failed;
252         }
253         return (kd);
254 failed:
255         /*
256          * Copy out the error if doing sane error semantics.
257          */
258         if (errout != 0)
259                 strlcpy(errout, kd->errbuf, _POSIX2_LINE_MAX);
260         (void)kvm_close(kd);
261         return (0);
262 }
263
264 kvm_t *
265 kvm_openfiles(uf, mf, sf, flag, errout)
266         const char *uf;
267         const char *mf;
268         const char *sf;
269         int flag;
270         char *errout;
271 {
272         register kvm_t *kd;
273
274         if ((kd = malloc(sizeof(*kd))) == NULL) {
275                 (void)strlcpy(errout, strerror(errno), _POSIX2_LINE_MAX);
276                 return (0);
277         }
278         memset(kd, 0, sizeof(*kd));
279         kd->program = 0;
280         return (_kvm_open(kd, uf, mf, flag, errout));
281 }
282
283 kvm_t *
284 kvm_open(uf, mf, sf, flag, errstr)
285         const char *uf;
286         const char *mf;
287         const char *sf;
288         int flag;
289         const char *errstr;
290 {
291         register kvm_t *kd;
292
293         if ((kd = malloc(sizeof(*kd))) == NULL) {
294                 if (errstr != NULL)
295                         (void)fprintf(stderr, "%s: %s\n",
296                                       errstr, strerror(errno));
297                 return (0);
298         }
299         memset(kd, 0, sizeof(*kd));
300         kd->program = errstr;
301         return (_kvm_open(kd, uf, mf, flag, NULL));
302 }
303
304 int
305 kvm_close(kd)
306         kvm_t *kd;
307 {
308         register int error = 0;
309
310         if (kd->pmfd >= 0)
311                 error |= close(kd->pmfd);
312         if (kd->vmfd >= 0)
313                 error |= close(kd->vmfd);
314         if (kd->nlfd >= 0)
315                 error |= close(kd->nlfd);
316         if (kd->vmst)
317                 _kvm_freevtop(kd);
318         if (kd->procbase != 0)
319                 free((void *)kd->procbase);
320         if (kd->argv != 0)
321                 free((void *)kd->argv);
322         free((void *)kd);
323
324         return (0);
325 }
326
327 int
328 kvm_nlist(kd, nl)
329         kvm_t *kd;
330         struct nlist *nl;
331 {
332         register struct nlist *p;
333         register int nvalid;
334         struct kld_sym_lookup lookup;
335
336         /*
337          * If we can't use the kld symbol lookup, revert to the
338          * slow library call.
339          */
340         if (!ISALIVE(kd))
341                 return (__fdnlist(kd->nlfd, nl));
342
343         /*
344          * We can use the kld lookup syscall.  Go through each nlist entry
345          * and look it up with a kldsym(2) syscall.
346          */
347         nvalid = 0;
348         for (p = nl; p->n_name && p->n_name[0]; ++p) {
349                 lookup.version = sizeof(lookup);
350                 lookup.symname = p->n_name;
351                 lookup.symvalue = 0;
352                 lookup.symsize = 0;
353
354                 if (lookup.symname[0] == '_')
355                         lookup.symname++;
356
357                 if (kldsym(0, KLDSYM_LOOKUP, &lookup) != -1) {
358                         p->n_type = N_TEXT;
359                         p->n_other = 0;
360                         p->n_desc = 0;
361                         p->n_value = lookup.symvalue;
362                         ++nvalid;
363                         /* lookup.symsize */
364                 }
365         }
366         /*
367          * Return the number of entries that weren't found.
368          */
369         return ((p - nl) - nvalid);
370 }
371
372 ssize_t
373 kvm_read(kd, kva, buf, len)
374         kvm_t *kd;
375         register u_long kva;
376         register void *buf;
377         register size_t len;
378 {
379         register int cc;
380         register void *cp;
381
382         if (ISALIVE(kd)) {
383                 /*
384                  * We're using /dev/kmem.  Just read straight from the
385                  * device and let the active kernel do the address translation.
386                  */
387                 errno = 0;
388                 if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) {
389                         _kvm_err(kd, 0, "invalid address (%x)", kva);
390                         return (-1);
391                 }
392                 cc = read(kd->vmfd, buf, len);
393                 if (cc < 0) {
394                         _kvm_syserr(kd, 0, "kvm_read");
395                         return (-1);
396                 } else if (cc < len)
397                         _kvm_err(kd, kd->program, "short read");
398                 return (cc);
399         } else {
400                 cp = buf;
401                 while (len > 0) {
402                         u_long pa;
403
404                         cc = _kvm_kvatop(kd, kva, &pa);
405                         if (cc == 0)
406                                 return (-1);
407                         if (cc > len)
408                                 cc = len;
409                         errno = 0;
410                         if (lseek(kd->pmfd, (off_t)pa, 0) == -1 && errno != 0) {
411                                 _kvm_syserr(kd, 0, _PATH_MEM);
412                                 break;
413                         }
414                         cc = read(kd->pmfd, cp, cc);
415                         if (cc < 0) {
416                                 _kvm_syserr(kd, kd->program, "kvm_read");
417                                 break;
418                         }
419                         /*
420                          * If kvm_kvatop returns a bogus value or our core
421                          * file is truncated, we might wind up seeking beyond
422                          * the end of the core file in which case the read will
423                          * return 0 (EOF).
424                          */
425                         if (cc == 0)
426                                 break;
427                         (char *)cp += cc;
428                         kva += cc;
429                         len -= cc;
430                 }
431                 return ((char *)cp - (char *)buf);
432         }
433         /* NOTREACHED */
434 }
435
436 ssize_t
437 kvm_write(kd, kva, buf, len)
438         kvm_t *kd;
439         register u_long kva;
440         register const void *buf;
441         register size_t len;
442 {
443         register int cc;
444
445         if (ISALIVE(kd)) {
446                 /*
447                  * Just like kvm_read, only we write.
448                  */
449                 errno = 0;
450                 if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) {
451                         _kvm_err(kd, 0, "invalid address (%x)", kva);
452                         return (-1);
453                 }
454                 cc = write(kd->vmfd, buf, len);
455                 if (cc < 0) {
456                         _kvm_syserr(kd, 0, "kvm_write");
457                         return (-1);
458                 } else if (cc < len)
459                         _kvm_err(kd, kd->program, "short write");
460                 return (cc);
461         } else {
462                 _kvm_err(kd, kd->program,
463                     "kvm_write not implemented for dead kernels");
464                 return (-1);
465         }
466         /* NOTREACHED */
467 }