1 | /* $NetBSD: kvm.c,v 1.104 2018/11/05 00:43:30 mrg Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1989, 1992, 1993 |
5 | * The Regents of the University of California. All rights reserved. |
6 | * |
7 | * This code is derived from software developed by the Computer Systems |
8 | * Engineering group at Lawrence Berkeley Laboratory under DARPA contract |
9 | * BG 91-66 and contributed to Berkeley. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * 3. Neither the name of the University nor the names of its contributors |
20 | * may be used to endorse or promote products derived from this software |
21 | * without specific prior written permission. |
22 | * |
23 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
33 | * SUCH DAMAGE. |
34 | */ |
35 | |
36 | #include <sys/cdefs.h> |
37 | #if defined(LIBC_SCCS) && !defined(lint) |
38 | #if 0 |
39 | static char sccsid[] = "@(#)kvm.c 8.2 (Berkeley) 2/13/94" ; |
40 | #else |
41 | __RCSID("$NetBSD: kvm.c,v 1.104 2018/11/05 00:43:30 mrg Exp $" ); |
42 | #endif |
43 | #endif /* LIBC_SCCS and not lint */ |
44 | |
45 | #include <sys/param.h> |
46 | #include <sys/lwp.h> |
47 | #include <sys/proc.h> |
48 | #include <sys/ioctl.h> |
49 | #include <sys/stat.h> |
50 | #include <sys/sysctl.h> |
51 | |
52 | #include <sys/core.h> |
53 | #include <sys/exec.h> |
54 | #include <sys/kcore.h> |
55 | #include <sys/ksyms.h> |
56 | #include <sys/types.h> |
57 | |
58 | #include <uvm/uvm_extern.h> |
59 | |
60 | #include <machine/cpu.h> |
61 | |
62 | #include <ctype.h> |
63 | #include <errno.h> |
64 | #include <fcntl.h> |
65 | #include <limits.h> |
66 | #include <nlist.h> |
67 | #include <paths.h> |
68 | #include <stdarg.h> |
69 | #include <stdio.h> |
70 | #include <stdlib.h> |
71 | #include <string.h> |
72 | #include <unistd.h> |
73 | #include <kvm.h> |
74 | |
75 | #include "kvm_private.h" |
76 | |
77 | static int _kvm_get_header(kvm_t *); |
78 | static kvm_t *_kvm_open(kvm_t *, const char *, const char *, |
79 | const char *, int, char *); |
80 | static int clear_gap(kvm_t *, bool (*)(void *, const void *, size_t), |
81 | void *, size_t); |
82 | static off_t Lseek(kvm_t *, int, off_t, int); |
83 | static ssize_t Pread(kvm_t *, int, void *, size_t, off_t); |
84 | |
85 | char * |
86 | kvm_geterr(kvm_t *kd) |
87 | { |
88 | return (kd->errbuf); |
89 | } |
90 | |
91 | const char * |
92 | kvm_getkernelname(kvm_t *kd) |
93 | { |
94 | return kd->kernelname; |
95 | } |
96 | |
97 | /* |
98 | * Report an error using printf style arguments. "program" is kd->program |
99 | * on hard errors, and 0 on soft errors, so that under sun error emulation, |
100 | * only hard errors are printed out (otherwise, programs like gdb will |
101 | * generate tons of error messages when trying to access bogus pointers). |
102 | */ |
103 | void |
104 | _kvm_err(kvm_t *kd, const char *program, const char *fmt, ...) |
105 | { |
106 | va_list ap; |
107 | |
108 | va_start(ap, fmt); |
109 | if (program != NULL) { |
110 | (void)fprintf(stderr, "%s: " , program); |
111 | (void)vfprintf(stderr, fmt, ap); |
112 | (void)fputc('\n', stderr); |
113 | } else |
114 | (void)vsnprintf(kd->errbuf, |
115 | sizeof(kd->errbuf), fmt, ap); |
116 | |
117 | va_end(ap); |
118 | } |
119 | |
120 | void |
121 | _kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...) |
122 | { |
123 | va_list ap; |
124 | size_t n; |
125 | |
126 | va_start(ap, fmt); |
127 | if (program != NULL) { |
128 | (void)fprintf(stderr, "%s: " , program); |
129 | (void)vfprintf(stderr, fmt, ap); |
130 | (void)fprintf(stderr, ": %s\n" , strerror(errno)); |
131 | } else { |
132 | char *cp = kd->errbuf; |
133 | |
134 | (void)vsnprintf(cp, sizeof(kd->errbuf), fmt, ap); |
135 | n = strlen(cp); |
136 | (void)snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s" , |
137 | strerror(errno)); |
138 | } |
139 | va_end(ap); |
140 | } |
141 | |
142 | void * |
143 | _kvm_malloc(kvm_t *kd, size_t n) |
144 | { |
145 | void *p; |
146 | |
147 | if ((p = malloc(n)) == NULL) |
148 | _kvm_err(kd, kd->program, "%s" , strerror(errno)); |
149 | return (p); |
150 | } |
151 | |
152 | /* |
153 | * Wrapper around the lseek(2) system call; calls _kvm_syserr() for us |
154 | * in the event of emergency. |
155 | */ |
156 | static off_t |
157 | Lseek(kvm_t *kd, int fd, off_t offset, int whence) |
158 | { |
159 | off_t off; |
160 | |
161 | errno = 0; |
162 | |
163 | if ((off = lseek(fd, offset, whence)) == -1 && errno != 0) { |
164 | _kvm_syserr(kd, kd->program, "Lseek" ); |
165 | return ((off_t)-1); |
166 | } |
167 | return (off); |
168 | } |
169 | |
170 | ssize_t |
171 | _kvm_pread(kvm_t *kd, int fd, void *buf, size_t size, off_t off) |
172 | { |
173 | ptrdiff_t moff; |
174 | void *newbuf; |
175 | size_t dsize; |
176 | ssize_t rv; |
177 | off_t doff; |
178 | |
179 | /* If aligned nothing to do. */ |
180 | if (((off % kd->fdalign) | (size % kd->fdalign)) == 0) { |
181 | return pread(fd, buf, size, off); |
182 | } |
183 | |
184 | /* |
185 | * Otherwise must buffer. We can't tolerate short reads in this |
186 | * case (lazy bum). |
187 | */ |
188 | moff = (ptrdiff_t)off % kd->fdalign; |
189 | doff = off - moff; |
190 | dsize = moff + size + kd->fdalign - 1; |
191 | dsize -= dsize % kd->fdalign; |
192 | if (kd->iobufsz < dsize) { |
193 | newbuf = realloc(kd->iobuf, dsize); |
194 | if (newbuf == NULL) { |
195 | _kvm_syserr(kd, 0, "cannot allocate I/O buffer" ); |
196 | return (-1); |
197 | } |
198 | kd->iobuf = newbuf; |
199 | kd->iobufsz = dsize; |
200 | } |
201 | rv = pread(fd, kd->iobuf, dsize, doff); |
202 | if (rv < size + moff) |
203 | return -1; |
204 | memcpy(buf, kd->iobuf + moff, size); |
205 | return size; |
206 | } |
207 | |
208 | /* |
209 | * Wrapper around the pread(2) system call; calls _kvm_syserr() for us |
210 | * in the event of emergency. |
211 | */ |
212 | static ssize_t |
213 | Pread(kvm_t *kd, int fd, void *buf, size_t nbytes, off_t offset) |
214 | { |
215 | ssize_t rv; |
216 | |
217 | errno = 0; |
218 | |
219 | if ((rv = _kvm_pread(kd, fd, buf, nbytes, offset)) != nbytes && |
220 | errno != 0) |
221 | _kvm_syserr(kd, kd->program, "Pread" ); |
222 | return (rv); |
223 | } |
224 | |
225 | static kvm_t * |
226 | _kvm_open(kvm_t *kd, const char *uf, const char *mf, const char *sf, int flag, |
227 | char *errout) |
228 | { |
229 | struct stat st; |
230 | int ufgiven; |
231 | |
232 | kd->pmfd = -1; |
233 | kd->vmfd = -1; |
234 | kd->swfd = -1; |
235 | kd->nlfd = -1; |
236 | kd->alive = KVM_ALIVE_DEAD; |
237 | kd->procbase = NULL; |
238 | kd->procbase_len = 0; |
239 | kd->procbase2 = NULL; |
240 | kd->procbase2_len = 0; |
241 | kd->lwpbase = NULL; |
242 | kd->lwpbase_len = 0; |
243 | kd->nbpg = getpagesize(); |
244 | kd->swapspc = NULL; |
245 | kd->argspc = NULL; |
246 | kd->argspc_len = 0; |
247 | kd->argbuf = NULL; |
248 | kd->argv = NULL; |
249 | kd->vmst = NULL; |
250 | kd->vm_page_buckets = NULL; |
251 | kd->kcore_hdr = NULL; |
252 | kd->cpu_dsize = 0; |
253 | kd->cpu_data = NULL; |
254 | kd->dump_off = 0; |
255 | kd->fdalign = 1; |
256 | kd->iobuf = NULL; |
257 | kd->iobufsz = 0; |
258 | kd->errbuf[0] = '\0'; |
259 | |
260 | if (flag & KVM_NO_FILES) { |
261 | kd->alive = KVM_ALIVE_SYSCTL; |
262 | return(kd); |
263 | } |
264 | |
265 | /* |
266 | * Call the MD open hook. This sets: |
267 | * usrstack, min_uva, max_uva |
268 | */ |
269 | if (_kvm_mdopen(kd)) { |
270 | _kvm_err(kd, kd->program, "md init failed" ); |
271 | goto failed; |
272 | } |
273 | |
274 | ufgiven = (uf != NULL); |
275 | if (!ufgiven) { |
276 | #ifdef CPU_BOOTED_KERNEL |
277 | /* 130 is 128 + '/' + '\0' */ |
278 | static char booted_kernel[130]; |
279 | int mib[2], rc; |
280 | size_t len; |
281 | |
282 | mib[0] = CTL_MACHDEP; |
283 | mib[1] = CPU_BOOTED_KERNEL; |
284 | booted_kernel[0] = '/'; |
285 | booted_kernel[1] = '\0'; |
286 | len = sizeof(booted_kernel) - 2; |
287 | rc = sysctl(&mib[0], 2, &booted_kernel[1], &len, NULL, 0); |
288 | booted_kernel[sizeof(booted_kernel) - 1] = '\0'; |
289 | uf = (booted_kernel[1] == '/') ? |
290 | &booted_kernel[1] : &booted_kernel[0]; |
291 | if (rc != -1) |
292 | rc = stat(uf, &st); |
293 | if (rc != -1 && !S_ISREG(st.st_mode)) |
294 | rc = -1; |
295 | if (rc == -1) |
296 | #endif /* CPU_BOOTED_KERNEL */ |
297 | uf = _PATH_UNIX; |
298 | } |
299 | else if (strlen(uf) >= MAXPATHLEN) { |
300 | _kvm_err(kd, kd->program, "exec file name too long" ); |
301 | goto failed; |
302 | } |
303 | if (flag & ~O_RDWR) { |
304 | _kvm_err(kd, kd->program, "bad flags arg" ); |
305 | goto failed; |
306 | } |
307 | if (mf == 0) |
308 | mf = _PATH_MEM; |
309 | if (sf == 0) |
310 | sf = _PATH_DRUM; |
311 | |
312 | /* |
313 | * Open the kernel namelist. If /dev/ksyms doesn't |
314 | * exist, open the current kernel. |
315 | */ |
316 | if (ufgiven == 0) |
317 | kd->nlfd = open(_PATH_KSYMS, O_RDONLY | O_CLOEXEC, 0); |
318 | if (kd->nlfd < 0) { |
319 | if ((kd->nlfd = open(uf, O_RDONLY | O_CLOEXEC, 0)) < 0) { |
320 | _kvm_syserr(kd, kd->program, "%s" , uf); |
321 | goto failed; |
322 | } |
323 | strlcpy(kd->kernelname, uf, sizeof(kd->kernelname)); |
324 | } else { |
325 | strlcpy(kd->kernelname, _PATH_KSYMS, sizeof(kd->kernelname)); |
326 | } |
327 | |
328 | if ((kd->pmfd = open(mf, flag | O_CLOEXEC, 0)) < 0) { |
329 | _kvm_syserr(kd, kd->program, "%s" , mf); |
330 | goto failed; |
331 | } |
332 | if (fstat(kd->pmfd, &st) < 0) { |
333 | _kvm_syserr(kd, kd->program, "%s" , mf); |
334 | goto failed; |
335 | } |
336 | if (S_ISCHR(st.st_mode) && strcmp(mf, _PATH_MEM) == 0) { |
337 | /* |
338 | * If this is /dev/mem, open kmem too. (Maybe we should |
339 | * make it work for either /dev/mem or /dev/kmem -- in either |
340 | * case you're working with a live kernel.) |
341 | */ |
342 | if ((kd->vmfd = open(_PATH_KMEM, flag | O_CLOEXEC, 0)) < 0) { |
343 | _kvm_syserr(kd, kd->program, "%s" , _PATH_KMEM); |
344 | goto failed; |
345 | } |
346 | kd->alive = KVM_ALIVE_FILES; |
347 | if ((kd->swfd = open(sf, flag | O_CLOEXEC, 0)) < 0) { |
348 | if (errno != ENXIO) { |
349 | _kvm_syserr(kd, kd->program, "%s" , sf); |
350 | goto failed; |
351 | } |
352 | /* swap is not configured? not fatal */ |
353 | } |
354 | } else { |
355 | kd->fdalign = DEV_BSIZE; /* XXX */ |
356 | /* |
357 | * This is a crash dump. |
358 | * Initialize the virtual address translation machinery. |
359 | * |
360 | * If there is no valid core header, fail silently here. |
361 | * The address translations however will fail without |
362 | * header. Things can be made to run by calling |
363 | * kvm_dump_mkheader() before doing any translation. |
364 | */ |
365 | if (_kvm_get_header(kd) == 0) { |
366 | if (_kvm_initvtop(kd) < 0) |
367 | goto failed; |
368 | } |
369 | } |
370 | return (kd); |
371 | failed: |
372 | /* |
373 | * Copy out the error if doing sane error semantics. |
374 | */ |
375 | if (errout != 0) |
376 | (void)strlcpy(errout, kd->errbuf, _POSIX2_LINE_MAX); |
377 | (void)kvm_close(kd); |
378 | return (0); |
379 | } |
380 | |
381 | /* |
382 | * The kernel dump file (from savecore) contains: |
383 | * kcore_hdr_t kcore_hdr; |
384 | * kcore_seg_t cpu_hdr; |
385 | * (opaque) cpu_data; (size is cpu_hdr.c_size) |
386 | * kcore_seg_t mem_hdr; |
387 | * (memory) mem_data; (size is mem_hdr.c_size) |
388 | * |
389 | * Note: khdr is padded to khdr.c_hdrsize; |
390 | * cpu_hdr and mem_hdr are padded to khdr.c_seghdrsize |
391 | */ |
392 | static int |
393 | (kvm_t *kd) |
394 | { |
395 | kcore_hdr_t kcore_hdr; |
396 | kcore_seg_t cpu_hdr; |
397 | kcore_seg_t mem_hdr; |
398 | size_t offset; |
399 | ssize_t sz; |
400 | |
401 | /* |
402 | * Read the kcore_hdr_t |
403 | */ |
404 | sz = Pread(kd, kd->pmfd, &kcore_hdr, sizeof(kcore_hdr), (off_t)0); |
405 | if (sz != sizeof(kcore_hdr)) |
406 | return (-1); |
407 | |
408 | /* |
409 | * Currently, we only support dump-files made by the current |
410 | * architecture... |
411 | */ |
412 | if ((CORE_GETMAGIC(kcore_hdr) != KCORE_MAGIC) || |
413 | (CORE_GETMID(kcore_hdr) != MID_MACHINE)) |
414 | return (-1); |
415 | |
416 | /* |
417 | * Currently, we only support exactly 2 segments: cpu-segment |
418 | * and data-segment in exactly that order. |
419 | */ |
420 | if (kcore_hdr.c_nseg != 2) |
421 | return (-1); |
422 | |
423 | /* |
424 | * Save away the kcore_hdr. All errors after this |
425 | * should do a to "goto fail" to deallocate things. |
426 | */ |
427 | kd->kcore_hdr = _kvm_malloc(kd, sizeof(kcore_hdr)); |
428 | memcpy(kd->kcore_hdr, &kcore_hdr, sizeof(kcore_hdr)); |
429 | offset = kcore_hdr.c_hdrsize; |
430 | |
431 | /* |
432 | * Read the CPU segment header |
433 | */ |
434 | sz = Pread(kd, kd->pmfd, &cpu_hdr, sizeof(cpu_hdr), (off_t)offset); |
435 | if (sz != sizeof(cpu_hdr)) |
436 | goto fail; |
437 | if ((CORE_GETMAGIC(cpu_hdr) != KCORESEG_MAGIC) || |
438 | (CORE_GETFLAG(cpu_hdr) != CORE_CPU)) |
439 | goto fail; |
440 | offset += kcore_hdr.c_seghdrsize; |
441 | |
442 | /* |
443 | * Read the CPU segment DATA. |
444 | */ |
445 | kd->cpu_dsize = cpu_hdr.c_size; |
446 | kd->cpu_data = _kvm_malloc(kd, cpu_hdr.c_size); |
447 | if (kd->cpu_data == NULL) |
448 | goto fail; |
449 | sz = Pread(kd, kd->pmfd, kd->cpu_data, cpu_hdr.c_size, (off_t)offset); |
450 | if (sz != cpu_hdr.c_size) |
451 | goto fail; |
452 | offset += cpu_hdr.c_size; |
453 | |
454 | /* |
455 | * Read the next segment header: data segment |
456 | */ |
457 | sz = Pread(kd, kd->pmfd, &mem_hdr, sizeof(mem_hdr), (off_t)offset); |
458 | if (sz != sizeof(mem_hdr)) |
459 | goto fail; |
460 | offset += kcore_hdr.c_seghdrsize; |
461 | |
462 | if ((CORE_GETMAGIC(mem_hdr) != KCORESEG_MAGIC) || |
463 | (CORE_GETFLAG(mem_hdr) != CORE_DATA)) |
464 | goto fail; |
465 | |
466 | kd->dump_off = offset; |
467 | return (0); |
468 | |
469 | fail: |
470 | if (kd->kcore_hdr != NULL) { |
471 | free(kd->kcore_hdr); |
472 | kd->kcore_hdr = NULL; |
473 | } |
474 | if (kd->cpu_data != NULL) { |
475 | free(kd->cpu_data); |
476 | kd->cpu_data = NULL; |
477 | kd->cpu_dsize = 0; |
478 | } |
479 | return (-1); |
480 | } |
481 | |
482 | /* |
483 | * The format while on the dump device is: (new format) |
484 | * kcore_seg_t cpu_hdr; |
485 | * (opaque) cpu_data; (size is cpu_hdr.c_size) |
486 | * kcore_seg_t mem_hdr; |
487 | * (memory) mem_data; (size is mem_hdr.c_size) |
488 | */ |
489 | int |
490 | (kvm_t *kd, off_t dump_off) |
491 | { |
492 | kcore_seg_t cpu_hdr; |
493 | size_t hdr_size; |
494 | ssize_t sz; |
495 | |
496 | if (kd->kcore_hdr != NULL) { |
497 | _kvm_err(kd, kd->program, "already has a dump header" ); |
498 | return (-1); |
499 | } |
500 | if (ISALIVE(kd)) { |
501 | _kvm_err(kd, kd->program, "don't use on live kernel" ); |
502 | return (-1); |
503 | } |
504 | |
505 | /* |
506 | * Validate new format crash dump |
507 | */ |
508 | sz = Pread(kd, kd->pmfd, &cpu_hdr, sizeof(cpu_hdr), dump_off); |
509 | if (sz != sizeof(cpu_hdr)) { |
510 | if (sz == -1) |
511 | _kvm_err(kd, 0, "read %zx bytes at offset %" PRIx64 |
512 | " for cpu_hdr failed: %s" , sizeof(cpu_hdr), |
513 | dump_off, strerror(errno)); |
514 | else |
515 | _kvm_err(kd, 0, "read %zx bytes at offset %" PRIx64 |
516 | " for cpu_hdr instead of requested %zu" , |
517 | sz, dump_off, sizeof(cpu_hdr)); |
518 | return (-1); |
519 | } |
520 | if ((CORE_GETMAGIC(cpu_hdr) != KCORE_MAGIC) |
521 | || (CORE_GETMID(cpu_hdr) != MID_MACHINE)) { |
522 | _kvm_err(kd, 0, "invalid magic in cpu_hdr" ); |
523 | return (0); |
524 | } |
525 | hdr_size = ALIGN(sizeof(cpu_hdr)); |
526 | |
527 | /* |
528 | * Read the CPU segment. |
529 | */ |
530 | kd->cpu_dsize = cpu_hdr.c_size; |
531 | kd->cpu_data = _kvm_malloc(kd, kd->cpu_dsize); |
532 | if (kd->cpu_data == NULL) { |
533 | _kvm_err(kd, kd->program, "no cpu_data" ); |
534 | goto fail; |
535 | } |
536 | sz = Pread(kd, kd->pmfd, kd->cpu_data, cpu_hdr.c_size, |
537 | dump_off + hdr_size); |
538 | if (sz != cpu_hdr.c_size) { |
539 | _kvm_err(kd, kd->program, "size %zu != cpu_hdr.csize %" PRIu32, |
540 | sz, cpu_hdr.c_size); |
541 | goto fail; |
542 | } |
543 | hdr_size += kd->cpu_dsize; |
544 | |
545 | /* |
546 | * Leave phys mem pointer at beginning of memory data |
547 | */ |
548 | kd->dump_off = dump_off + hdr_size; |
549 | if (Lseek(kd, kd->pmfd, kd->dump_off, SEEK_SET) == -1) { |
550 | _kvm_err(kd, kd->program, "failed to seek to %" PRId64, |
551 | (int64_t)kd->dump_off); |
552 | goto fail; |
553 | } |
554 | |
555 | /* |
556 | * Create a kcore_hdr. |
557 | */ |
558 | kd->kcore_hdr = _kvm_malloc(kd, sizeof(kcore_hdr_t)); |
559 | if (kd->kcore_hdr == NULL) { |
560 | _kvm_err(kd, kd->program, "failed to allocate header" ); |
561 | goto fail; |
562 | } |
563 | |
564 | kd->kcore_hdr->c_hdrsize = ALIGN(sizeof(kcore_hdr_t)); |
565 | kd->kcore_hdr->c_seghdrsize = ALIGN(sizeof(kcore_seg_t)); |
566 | kd->kcore_hdr->c_nseg = 2; |
567 | CORE_SETMAGIC(*(kd->kcore_hdr), KCORE_MAGIC, MID_MACHINE,0); |
568 | |
569 | /* |
570 | * Now that we have a valid header, enable translations. |
571 | */ |
572 | if (_kvm_initvtop(kd) == 0) |
573 | /* Success */ |
574 | return (hdr_size); |
575 | |
576 | fail: |
577 | if (kd->kcore_hdr != NULL) { |
578 | free(kd->kcore_hdr); |
579 | kd->kcore_hdr = NULL; |
580 | } |
581 | if (kd->cpu_data != NULL) { |
582 | free(kd->cpu_data); |
583 | kd->cpu_data = NULL; |
584 | kd->cpu_dsize = 0; |
585 | } |
586 | return (-1); |
587 | } |
588 | |
589 | static int |
590 | clear_gap(kvm_t *kd, bool (*write_buf)(void *, const void *, size_t), |
591 | void *cookie, size_t size) |
592 | { |
593 | char buf[1024]; |
594 | size_t len; |
595 | |
596 | (void)memset(buf, 0, size > sizeof(buf) ? sizeof(buf) : size); |
597 | |
598 | while (size > 0) { |
599 | len = size > sizeof(buf) ? sizeof(buf) : size; |
600 | if (!(*write_buf)(cookie, buf, len)) { |
601 | _kvm_syserr(kd, kd->program, "clear_gap" ); |
602 | return -1; |
603 | } |
604 | size -= len; |
605 | } |
606 | |
607 | return 0; |
608 | } |
609 | |
610 | /* |
611 | * Write the dump header by calling write_buf with cookie as first argument. |
612 | */ |
613 | int |
614 | (kvm_t *kd, bool (*write_buf)(void *, const void *, size_t), |
615 | void *cookie, int dumpsize) |
616 | { |
617 | kcore_seg_t seghdr; |
618 | long offset; |
619 | size_t gap; |
620 | |
621 | if (kd->kcore_hdr == NULL || kd->cpu_data == NULL) { |
622 | _kvm_err(kd, kd->program, "no valid dump header(s)" ); |
623 | return (-1); |
624 | } |
625 | |
626 | /* |
627 | * Write the generic header |
628 | */ |
629 | offset = 0; |
630 | if (!(*write_buf)(cookie, kd->kcore_hdr, sizeof(kcore_hdr_t))) { |
631 | _kvm_syserr(kd, kd->program, "kvm_dump_header" ); |
632 | return (-1); |
633 | } |
634 | offset += kd->kcore_hdr->c_hdrsize; |
635 | gap = kd->kcore_hdr->c_hdrsize - sizeof(kcore_hdr_t); |
636 | if (clear_gap(kd, write_buf, cookie, gap) == -1) |
637 | return (-1); |
638 | |
639 | /* |
640 | * Write the CPU header |
641 | */ |
642 | CORE_SETMAGIC(seghdr, KCORESEG_MAGIC, 0, CORE_CPU); |
643 | seghdr.c_size = ALIGN(kd->cpu_dsize); |
644 | if (!(*write_buf)(cookie, &seghdr, sizeof(seghdr))) { |
645 | _kvm_syserr(kd, kd->program, "kvm_dump_header" ); |
646 | return (-1); |
647 | } |
648 | offset += kd->kcore_hdr->c_seghdrsize; |
649 | gap = kd->kcore_hdr->c_seghdrsize - sizeof(seghdr); |
650 | if (clear_gap(kd, write_buf, cookie, gap) == -1) |
651 | return (-1); |
652 | |
653 | if (!(*write_buf)(cookie, kd->cpu_data, kd->cpu_dsize)) { |
654 | _kvm_syserr(kd, kd->program, "kvm_dump_header" ); |
655 | return (-1); |
656 | } |
657 | offset += seghdr.c_size; |
658 | gap = seghdr.c_size - kd->cpu_dsize; |
659 | if (clear_gap(kd, write_buf, cookie, gap) == -1) |
660 | return (-1); |
661 | |
662 | /* |
663 | * Write the actual dump data segment header |
664 | */ |
665 | CORE_SETMAGIC(seghdr, KCORESEG_MAGIC, 0, CORE_DATA); |
666 | seghdr.c_size = dumpsize; |
667 | if (!(*write_buf)(cookie, &seghdr, sizeof(seghdr))) { |
668 | _kvm_syserr(kd, kd->program, "kvm_dump_header" ); |
669 | return (-1); |
670 | } |
671 | offset += kd->kcore_hdr->c_seghdrsize; |
672 | gap = kd->kcore_hdr->c_seghdrsize - sizeof(seghdr); |
673 | if (clear_gap(kd, write_buf, cookie, gap) == -1) |
674 | return (-1); |
675 | |
676 | return (int)offset; |
677 | } |
678 | |
679 | static bool |
680 | (void *cookie, const void *buf, size_t len) |
681 | { |
682 | return fwrite(buf, len, 1, (FILE *)cookie) == 1; |
683 | } |
684 | |
685 | int |
686 | (kvm_t *kd, FILE *fp, int dumpsize) |
687 | { |
688 | return kvm_dump_header(kd, kvm_dump_header_stdio, fp, dumpsize); |
689 | } |
690 | |
691 | kvm_t * |
692 | kvm_openfiles(const char *uf, const char *mf, const char *sf, |
693 | int flag, char *errout) |
694 | { |
695 | kvm_t *kd; |
696 | |
697 | if ((kd = malloc(sizeof(*kd))) == NULL) { |
698 | (void)strlcpy(errout, strerror(errno), _POSIX2_LINE_MAX); |
699 | return (0); |
700 | } |
701 | kd->program = 0; |
702 | return (_kvm_open(kd, uf, mf, sf, flag, errout)); |
703 | } |
704 | |
705 | kvm_t * |
706 | kvm_open(const char *uf, const char *mf, const char *sf, int flag, |
707 | const char *program) |
708 | { |
709 | kvm_t *kd; |
710 | |
711 | if ((kd = malloc(sizeof(*kd))) == NULL) { |
712 | (void)fprintf(stderr, "%s: %s\n" , |
713 | program ? program : getprogname(), strerror(errno)); |
714 | return (0); |
715 | } |
716 | kd->program = program; |
717 | return (_kvm_open(kd, uf, mf, sf, flag, NULL)); |
718 | } |
719 | |
720 | int |
721 | kvm_close(kvm_t *kd) |
722 | { |
723 | int error = 0; |
724 | |
725 | if (kd->pmfd >= 0) |
726 | error |= close(kd->pmfd); |
727 | if (kd->vmfd >= 0) |
728 | error |= close(kd->vmfd); |
729 | if (kd->nlfd >= 0) |
730 | error |= close(kd->nlfd); |
731 | if (kd->swfd >= 0) |
732 | error |= close(kd->swfd); |
733 | if (kd->vmst) |
734 | _kvm_freevtop(kd); |
735 | kd->cpu_dsize = 0; |
736 | if (kd->cpu_data != NULL) |
737 | free(kd->cpu_data); |
738 | if (kd->kcore_hdr != NULL) |
739 | free(kd->kcore_hdr); |
740 | if (kd->procbase != 0) |
741 | free(kd->procbase); |
742 | if (kd->procbase2 != 0) |
743 | free(kd->procbase2); |
744 | if (kd->lwpbase != 0) |
745 | free(kd->lwpbase); |
746 | if (kd->swapspc != 0) |
747 | free(kd->swapspc); |
748 | if (kd->argspc != 0) |
749 | free(kd->argspc); |
750 | if (kd->argbuf != 0) |
751 | free(kd->argbuf); |
752 | if (kd->argv != 0) |
753 | free(kd->argv); |
754 | if (kd->iobuf != 0) |
755 | free(kd->iobuf); |
756 | free(kd); |
757 | |
758 | return (error); |
759 | } |
760 | |
761 | int |
762 | kvm_nlist(kvm_t *kd, struct nlist *nl) |
763 | { |
764 | int rv; |
765 | |
766 | /* |
767 | * Call the nlist(3) routines to retrieve the given namelist. |
768 | */ |
769 | rv = __fdnlist(kd->nlfd, nl); |
770 | |
771 | if (rv == -1) |
772 | _kvm_err(kd, 0, "bad namelist" ); |
773 | |
774 | return (rv); |
775 | } |
776 | |
777 | int |
778 | kvm_dump_inval(kvm_t *kd) |
779 | { |
780 | struct nlist nl[2]; |
781 | paddr_t pa; |
782 | size_t dsize; |
783 | off_t doff; |
784 | void *newbuf; |
785 | |
786 | if (ISALIVE(kd)) { |
787 | _kvm_err(kd, kd->program, "clearing dump on live kernel" ); |
788 | return (-1); |
789 | } |
790 | nl[0].n_name = "_dumpmag" ; |
791 | nl[1].n_name = NULL; |
792 | |
793 | if (kvm_nlist(kd, nl) == -1) { |
794 | _kvm_err(kd, 0, "bad namelist" ); |
795 | return (-1); |
796 | } |
797 | if (_kvm_kvatop(kd, (vaddr_t)nl[0].n_value, &pa) == 0) |
798 | return (-1); |
799 | |
800 | errno = 0; |
801 | dsize = MAX(kd->fdalign, sizeof(u_long)); |
802 | if (kd->iobufsz < dsize) { |
803 | newbuf = realloc(kd->iobuf, dsize); |
804 | if (newbuf == NULL) { |
805 | _kvm_syserr(kd, 0, "cannot allocate I/O buffer" ); |
806 | return (-1); |
807 | } |
808 | kd->iobuf = newbuf; |
809 | kd->iobufsz = dsize; |
810 | } |
811 | memset(kd->iobuf, 0, dsize); |
812 | doff = _kvm_pa2off(kd, pa); |
813 | doff -= doff % kd->fdalign; |
814 | if (pwrite(kd->pmfd, kd->iobuf, dsize, doff) == -1) { |
815 | _kvm_syserr(kd, 0, "cannot invalidate dump - pwrite" ); |
816 | return (-1); |
817 | } |
818 | return (0); |
819 | } |
820 | |
821 | ssize_t |
822 | kvm_read(kvm_t *kd, u_long kva, void *buf, size_t len) |
823 | { |
824 | int cc; |
825 | void *cp; |
826 | |
827 | if (ISKMEM(kd)) { |
828 | /* |
829 | * We're using /dev/kmem. Just read straight from the |
830 | * device and let the active kernel do the address translation. |
831 | */ |
832 | errno = 0; |
833 | cc = _kvm_pread(kd, kd->vmfd, buf, len, (off_t)kva); |
834 | if (cc < 0) { |
835 | _kvm_syserr(kd, 0, "kvm_read" ); |
836 | return (-1); |
837 | } else if (cc < len) |
838 | _kvm_err(kd, kd->program, "short read" ); |
839 | return (cc); |
840 | } else if (ISSYSCTL(kd)) { |
841 | _kvm_err(kd, kd->program, "kvm_open called with KVM_NO_FILES, " |
842 | "can't use kvm_read" ); |
843 | return (-1); |
844 | } else { |
845 | if ((kd->kcore_hdr == NULL) || (kd->cpu_data == NULL)) { |
846 | _kvm_err(kd, kd->program, "no valid dump header" ); |
847 | return (-1); |
848 | } |
849 | cp = buf; |
850 | while (len > 0) { |
851 | paddr_t pa; |
852 | off_t foff; |
853 | |
854 | cc = _kvm_kvatop(kd, (vaddr_t)kva, &pa); |
855 | if (cc == 0) { |
856 | _kvm_err(kd, kd->program, "_kvm_kvatop(%lx)" , kva); |
857 | return (-1); |
858 | } |
859 | if (cc > len) |
860 | cc = len; |
861 | foff = _kvm_pa2off(kd, pa); |
862 | errno = 0; |
863 | cc = _kvm_pread(kd, kd->pmfd, cp, (size_t)cc, foff); |
864 | if (cc < 0) { |
865 | _kvm_syserr(kd, kd->program, "kvm_read" ); |
866 | break; |
867 | } |
868 | /* |
869 | * If kvm_kvatop returns a bogus value or our core |
870 | * file is truncated, we might wind up seeking beyond |
871 | * the end of the core file in which case the read will |
872 | * return 0 (EOF). |
873 | */ |
874 | if (cc == 0) |
875 | break; |
876 | cp = (char *)cp + cc; |
877 | kva += cc; |
878 | len -= cc; |
879 | } |
880 | return ((char *)cp - (char *)buf); |
881 | } |
882 | /* NOTREACHED */ |
883 | } |
884 | |
885 | ssize_t |
886 | kvm_write(kvm_t *kd, u_long kva, const void *buf, size_t len) |
887 | { |
888 | int cc; |
889 | |
890 | if (ISKMEM(kd)) { |
891 | /* |
892 | * Just like kvm_read, only we write. |
893 | */ |
894 | errno = 0; |
895 | cc = pwrite(kd->vmfd, buf, len, (off_t)kva); |
896 | if (cc < 0) { |
897 | _kvm_syserr(kd, 0, "kvm_write" ); |
898 | return (-1); |
899 | } else if (cc < len) |
900 | _kvm_err(kd, kd->program, "short write" ); |
901 | return (cc); |
902 | } else if (ISSYSCTL(kd)) { |
903 | _kvm_err(kd, kd->program, "kvm_open called with KVM_NO_FILES, " |
904 | "can't use kvm_write" ); |
905 | return (-1); |
906 | } else { |
907 | _kvm_err(kd, kd->program, |
908 | "kvm_write not implemented for dead kernels" ); |
909 | return (-1); |
910 | } |
911 | /* NOTREACHED */ |
912 | } |
913 | |