1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <kvm.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <unistd.h>
34 #include <limits.h>
35 #include <fcntl.h>
36 #include <strings.h>
37 #include <sys/mem.h>
38 #include <sys/stat.h>
39 #include <sys/mman.h>
40 #include <sys/dumphdr.h>
41 #include <sys/sysmacros.h>
42
43 struct _kvmd {
44 struct dumphdr kvm_dump;
45 char *kvm_debug;
46 int kvm_openflag;
47 int kvm_corefd;
48 int kvm_kmemfd;
49 int kvm_memfd;
50 size_t kvm_coremapsize;
51 char *kvm_core;
52 dump_map_t *kvm_map;
53 pfn_t *kvm_pfn;
54 struct as *kvm_kas;
55 proc_t *kvm_practive;
56 pid_t kvm_pid;
57 char kvm_namelist[MAXNAMELEN + 1];
58 proc_t kvm_proc;
59 };
60
61 #define PREAD (ssize_t (*)(int, void *, size_t, offset_t))pread64
62 #define PWRITE (ssize_t (*)(int, void *, size_t, offset_t))pwrite64
63
64 static kvm_t *
fail(kvm_t * kd,const char * err,const char * message,...)65 fail(kvm_t *kd, const char *err, const char *message, ...)
66 {
67 va_list args;
68
69 va_start(args, message);
70 if (err || (kd && kd->kvm_debug)) {
71 (void) fprintf(stderr, "%s: ", err ? err : "KVM_DEBUG");
72 (void) vfprintf(stderr, message, args);
73 (void) fprintf(stderr, "\n");
74 }
75 va_end(args);
76 if (kd != NULL)
77 (void) kvm_close(kd);
78 return (NULL);
79 }
80
81 /*ARGSUSED*/
82 kvm_t *
kvm_open(const char * namelist,const char * corefile,const char * swapfile,int flag,const char * err)83 kvm_open(const char *namelist, const char *corefile, const char *swapfile,
84 int flag, const char *err)
85 {
86 kvm_t *kd;
87 struct stat64 memstat, kmemstat, allkmemstat, corestat;
88 struct nlist nl[3] = { { "kas" }, { "practive" }, { "" } };
89
90 if ((kd = calloc(1, sizeof (kvm_t))) == NULL)
91 return (fail(NULL, err, "cannot allocate space for kvm_t"));
92
93 kd->kvm_corefd = kd->kvm_kmemfd = kd->kvm_memfd = -1;
94 kd->kvm_debug = getenv("KVM_DEBUG");
95
96 if ((kd->kvm_openflag = flag) != O_RDONLY && flag != O_RDWR)
97 return (fail(kd, err, "illegal flag 0x%x to kvm_open()", flag));
98
99 if (corefile == NULL)
100 corefile = "/dev/kmem";
101
102 if (stat64(corefile, &corestat) == -1)
103 return (fail(kd, err, "cannot stat %s", corefile));
104
105 if (S_ISCHR(corestat.st_mode)) {
106 if (stat64("/dev/mem", &memstat) == -1)
107 return (fail(kd, err, "cannot stat /dev/mem"));
108
109 if (stat64("/dev/kmem", &kmemstat) == -1)
110 return (fail(kd, err, "cannot stat /dev/kmem"));
111
112 if (stat64("/dev/allkmem", &allkmemstat) == -1)
113 return (fail(kd, err, "cannot stat /dev/allkmem"));
114 if (corestat.st_rdev == memstat.st_rdev ||
115 corestat.st_rdev == kmemstat.st_rdev ||
116 corestat.st_rdev == allkmemstat.st_rdev) {
117 char *kmem = (corestat.st_rdev == allkmemstat.st_rdev ?
118 "/dev/allkmem" : "/dev/kmem");
119
120 if ((kd->kvm_kmemfd = open64(kmem, flag)) == -1)
121 return (fail(kd, err, "cannot open %s", kmem));
122 if ((kd->kvm_memfd = open64("/dev/mem", flag)) == -1)
123 return (fail(kd, err, "cannot open /dev/mem"));
124 }
125 } else {
126 if ((kd->kvm_corefd = open64(corefile, flag)) == -1)
127 return (fail(kd, err, "cannot open %s", corefile));
128 if (pread64(kd->kvm_corefd, &kd->kvm_dump,
129 sizeof (kd->kvm_dump), 0) != sizeof (kd->kvm_dump))
130 return (fail(kd, err, "cannot read dump header"));
131 if (kd->kvm_dump.dump_magic != DUMP_MAGIC)
132 return (fail(kd, err, "%s is not a kernel core file "
133 "(bad magic number %x)", corefile,
134 kd->kvm_dump.dump_magic));
135 if (kd->kvm_dump.dump_version != DUMP_VERSION)
136 return (fail(kd, err,
137 "libkvm version (%u) != corefile version (%u)",
138 DUMP_VERSION, kd->kvm_dump.dump_version));
139 if (kd->kvm_dump.dump_wordsize != DUMP_WORDSIZE)
140 return (fail(kd, err, "%s is a %d-bit core file - "
141 "cannot examine with %d-bit libkvm", corefile,
142 kd->kvm_dump.dump_wordsize, DUMP_WORDSIZE));
143 /*
144 * We try to mmap(2) the entire corefile for performance
145 * (so we can use bcopy(3C) rather than pread(2)). Failing
146 * that, we insist on at least mmap(2)ing the dump map.
147 */
148 kd->kvm_coremapsize = (size_t)corestat.st_size;
149 if (corestat.st_size > LONG_MAX ||
150 (kd->kvm_core = mmap64(0, kd->kvm_coremapsize,
151 PROT_READ, MAP_SHARED, kd->kvm_corefd, 0)) == MAP_FAILED) {
152 kd->kvm_coremapsize = kd->kvm_dump.dump_data;
153 if ((kd->kvm_core = mmap64(0, kd->kvm_coremapsize,
154 PROT_READ, MAP_SHARED, kd->kvm_corefd, 0)) ==
155 MAP_FAILED)
156 return (fail(kd, err, "cannot mmap corefile"));
157 }
158 kd->kvm_map = (void *)(kd->kvm_core + kd->kvm_dump.dump_map);
159 kd->kvm_pfn = (void *)(kd->kvm_core + kd->kvm_dump.dump_pfn);
160 }
161
162 if (namelist == NULL)
163 namelist = "/dev/ksyms";
164
165 (void) strncpy(kd->kvm_namelist, namelist, MAXNAMELEN);
166
167 if (kvm_nlist(kd, nl) == -1)
168 return (fail(kd, err, "%s is not a %d-bit kernel namelist",
169 namelist, DUMP_WORDSIZE));
170
171 kd->kvm_kas = (struct as *)nl[0].n_value;
172 kd->kvm_practive = (proc_t *)nl[1].n_value;
173
174 (void) kvm_setproc(kd);
175 return (kd);
176 }
177
178 int
kvm_close(kvm_t * kd)179 kvm_close(kvm_t *kd)
180 {
181 if (kd->kvm_core != NULL && kd->kvm_core != MAP_FAILED)
182 (void) munmap(kd->kvm_core, kd->kvm_coremapsize);
183 if (kd->kvm_corefd != -1)
184 (void) close(kd->kvm_corefd);
185 if (kd->kvm_kmemfd != -1)
186 (void) close(kd->kvm_kmemfd);
187 if (kd->kvm_memfd != -1)
188 (void) close(kd->kvm_memfd);
189 free(kd);
190 return (0);
191 }
192
193 int
kvm_nlist(kvm_t * kd,struct nlist nl[])194 kvm_nlist(kvm_t *kd, struct nlist nl[])
195 {
196 return (nlist(kd->kvm_namelist, nl));
197 }
198
199 static offset_t
kvm_lookup(kvm_t * kd,struct as * as,uint64_t addr)200 kvm_lookup(kvm_t *kd, struct as *as, uint64_t addr)
201 {
202 uintptr_t pageoff = addr & (kd->kvm_dump.dump_pagesize - 1);
203 uint64_t page = addr - pageoff;
204 offset_t off = 0;
205
206 if (kd->kvm_debug)
207 fprintf(stderr, "kvm_lookup(%p, %llx):", (void *)as, addr);
208
209 if (as == NULL) { /* physical addressing mode */
210 long first = 0;
211 long last = kd->kvm_dump.dump_npages - 1;
212 pfn_t target = (pfn_t)(page >> kd->kvm_dump.dump_pageshift);
213 while (last >= first) {
214 long middle = (first + last) / 2;
215 pfn_t pfn = kd->kvm_pfn[middle];
216 if (kd->kvm_debug)
217 fprintf(stderr, " %ld ->", middle);
218 if (pfn == target) {
219 off = kd->kvm_dump.dump_data + pageoff +
220 ((uint64_t)middle <<
221 kd->kvm_dump.dump_pageshift);
222 break;
223 }
224 if (pfn < target)
225 first = middle + 1;
226 else
227 last = middle - 1;
228 }
229 } else {
230 long hash = DUMP_HASH(&kd->kvm_dump, as, page);
231 off = kd->kvm_map[hash].dm_first;
232 while (off != 0) {
233 dump_map_t *dmp = (void *)(kd->kvm_core + off);
234 if (kd->kvm_debug)
235 fprintf(stderr, " %llx ->", off);
236 if (dmp < kd->kvm_map ||
237 dmp > kd->kvm_map + kd->kvm_dump.dump_hashmask ||
238 (off & (sizeof (offset_t) - 1)) != 0 ||
239 DUMP_HASH(&kd->kvm_dump, dmp->dm_as, dmp->dm_va) !=
240 hash) {
241 if (kd->kvm_debug)
242 fprintf(stderr, " dump map corrupt\n");
243 return (0);
244 }
245 if (dmp->dm_va == page && dmp->dm_as == as) {
246 off = dmp->dm_data + pageoff;
247 break;
248 }
249 off = dmp->dm_next;
250 }
251 }
252 if (kd->kvm_debug)
253 fprintf(stderr, "%s found: %llx\n", off ? "" : " not", off);
254 return (off);
255 }
256
257 static ssize_t
kvm_rw(kvm_t * kd,uint64_t addr,void * buf,size_t size,struct as * as,ssize_t (* prw)(int,void *,size_t,offset_t))258 kvm_rw(kvm_t *kd, uint64_t addr, void *buf, size_t size,
259 struct as *as, ssize_t (*prw)(int, void *, size_t, offset_t))
260 {
261 offset_t off;
262 size_t resid = size;
263
264 /*
265 * read/write of zero bytes always succeeds
266 */
267 if (size == 0)
268 return (0);
269
270 if (kd->kvm_core == NULL) {
271 char procbuf[100];
272 int procfd;
273 ssize_t rval;
274
275 if (as == kd->kvm_kas)
276 return (prw(kd->kvm_kmemfd, buf, size, addr));
277 if (as == NULL)
278 return (prw(kd->kvm_memfd, buf, size, addr));
279
280 (void) sprintf(procbuf, "/proc/%ld/as", kd->kvm_pid);
281 if ((procfd = open64(procbuf, kd->kvm_openflag)) == -1)
282 return (-1);
283 rval = prw(procfd, buf, size, addr);
284 (void) close(procfd);
285 return (rval);
286 }
287
288 while (resid != 0) {
289 uintptr_t pageoff = addr & (kd->kvm_dump.dump_pagesize - 1);
290 ssize_t len = MIN(resid, kd->kvm_dump.dump_pagesize - pageoff);
291
292 if ((off = kvm_lookup(kd, as, addr)) == 0)
293 break;
294
295 if (prw == PREAD && off < kd->kvm_coremapsize)
296 bcopy(kd->kvm_core + off, buf, len);
297 else if ((len = prw(kd->kvm_corefd, buf, len, off)) <= 0)
298 break;
299 resid -= len;
300 addr += len;
301 buf = (char *)buf + len;
302 }
303 return (resid < size ? size - resid : -1);
304 }
305
306 ssize_t
kvm_read(kvm_t * kd,uintptr_t addr,void * buf,size_t size)307 kvm_read(kvm_t *kd, uintptr_t addr, void *buf, size_t size)
308 {
309 return (kvm_rw(kd, addr, buf, size, kd->kvm_kas, PREAD));
310 }
311
312 ssize_t
kvm_kread(kvm_t * kd,uintptr_t addr,void * buf,size_t size)313 kvm_kread(kvm_t *kd, uintptr_t addr, void *buf, size_t size)
314 {
315 return (kvm_rw(kd, addr, buf, size, kd->kvm_kas, PREAD));
316 }
317
318 ssize_t
kvm_uread(kvm_t * kd,uintptr_t addr,void * buf,size_t size)319 kvm_uread(kvm_t *kd, uintptr_t addr, void *buf, size_t size)
320 {
321 return (kvm_rw(kd, addr, buf, size, kd->kvm_proc.p_as, PREAD));
322 }
323
324 ssize_t
kvm_aread(kvm_t * kd,uintptr_t addr,void * buf,size_t size,struct as * as)325 kvm_aread(kvm_t *kd, uintptr_t addr, void *buf, size_t size, struct as *as)
326 {
327 return (kvm_rw(kd, addr, buf, size, as, PREAD));
328 }
329
330 ssize_t
kvm_pread(kvm_t * kd,uint64_t addr,void * buf,size_t size)331 kvm_pread(kvm_t *kd, uint64_t addr, void *buf, size_t size)
332 {
333 return (kvm_rw(kd, addr, buf, size, NULL, PREAD));
334 }
335
336 ssize_t
kvm_write(kvm_t * kd,uintptr_t addr,const void * buf,size_t size)337 kvm_write(kvm_t *kd, uintptr_t addr, const void *buf, size_t size)
338 {
339 return (kvm_rw(kd, addr, (void *)buf, size, kd->kvm_kas, PWRITE));
340 }
341
342 ssize_t
kvm_kwrite(kvm_t * kd,uintptr_t addr,const void * buf,size_t size)343 kvm_kwrite(kvm_t *kd, uintptr_t addr, const void *buf, size_t size)
344 {
345 return (kvm_rw(kd, addr, (void *)buf, size, kd->kvm_kas, PWRITE));
346 }
347
348 ssize_t
kvm_uwrite(kvm_t * kd,uintptr_t addr,const void * buf,size_t size)349 kvm_uwrite(kvm_t *kd, uintptr_t addr, const void *buf, size_t size)
350 {
351 return (kvm_rw(kd, addr, (void *)buf, size, kd->kvm_proc.p_as, PWRITE));
352 }
353
354 ssize_t
kvm_awrite(kvm_t * kd,uintptr_t addr,const void * buf,size_t size,struct as * as)355 kvm_awrite(kvm_t *kd, uintptr_t addr, const void *buf, size_t size,
356 struct as *as)
357 {
358 return (kvm_rw(kd, addr, (void *)buf, size, as, PWRITE));
359 }
360
361 ssize_t
kvm_pwrite(kvm_t * kd,uint64_t addr,const void * buf,size_t size)362 kvm_pwrite(kvm_t *kd, uint64_t addr, const void *buf, size_t size)
363 {
364 return (kvm_rw(kd, addr, (void *)buf, size, NULL, PWRITE));
365 }
366
367 uint64_t
kvm_physaddr(kvm_t * kd,struct as * as,uintptr_t addr)368 kvm_physaddr(kvm_t *kd, struct as *as, uintptr_t addr)
369 {
370 mem_vtop_t mem_vtop;
371 offset_t off;
372
373 if (kd->kvm_core == NULL) {
374 mem_vtop.m_as = as;
375 mem_vtop.m_va = (void *)addr;
376 if (ioctl(kd->kvm_kmemfd, MEM_VTOP, &mem_vtop) == 0)
377 return ((uint64_t)mem_vtop.m_pfn * getpagesize() +
378 (addr & (getpagesize() - 1)));
379 } else {
380 if ((off = kvm_lookup(kd, as, addr)) != 0) {
381 long pfn_index =
382 (u_offset_t)(off - kd->kvm_dump.dump_data) >>
383 kd->kvm_dump.dump_pageshift;
384 return (((uint64_t)kd->kvm_pfn[pfn_index] <<
385 kd->kvm_dump.dump_pageshift) +
386 (addr & (kd->kvm_dump.dump_pagesize - 1)));
387 }
388 }
389 return (-1ULL);
390 }
391
392 struct proc *
kvm_getproc(kvm_t * kd,pid_t pid)393 kvm_getproc(kvm_t *kd, pid_t pid)
394 {
395 (void) kvm_setproc(kd);
396 while (kvm_nextproc(kd) != NULL)
397 if (kd->kvm_pid == pid)
398 return (&kd->kvm_proc);
399 return (NULL);
400 }
401
402 struct proc *
kvm_nextproc(kvm_t * kd)403 kvm_nextproc(kvm_t *kd)
404 {
405 if (kd->kvm_proc.p_next == NULL ||
406 kvm_kread(kd, (uintptr_t)kd->kvm_proc.p_next,
407 &kd->kvm_proc, sizeof (proc_t)) != sizeof (proc_t) ||
408 kvm_kread(kd, (uintptr_t)&kd->kvm_proc.p_pidp->pid_id,
409 &kd->kvm_pid, sizeof (pid_t)) != sizeof (pid_t))
410 return (NULL);
411
412 return (&kd->kvm_proc);
413 }
414
415 int
kvm_setproc(kvm_t * kd)416 kvm_setproc(kvm_t *kd)
417 {
418 (void) kvm_kread(kd, (uintptr_t)kd->kvm_practive,
419 &kd->kvm_proc.p_next, sizeof (proc_t *));
420 kd->kvm_pid = -1;
421 return (0);
422 }
423
424 /*ARGSUSED*/
425 struct user *
kvm_getu(kvm_t * kd,struct proc * p)426 kvm_getu(kvm_t *kd, struct proc *p)
427 {
428 return (&p->p_user);
429 }
430