Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /*- |
2 | * Copyright (c) 1989 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | * | |
15637ed4 RG |
33 | */ |
34 | ||
35 | #if defined(LIBC_SCCS) && !defined(lint) | |
36 | static char sccsid[] = "@(#)kvm.c 5.18 (Berkeley) 5/7/91"; | |
37 | #endif /* LIBC_SCCS and not lint */ | |
38 | ||
8387d405 | 39 | #define DEBUG 0 |
15637ed4 RG |
40 | |
41 | #include <sys/param.h> | |
42 | #include <sys/user.h> | |
43 | #include <sys/proc.h> | |
44 | #include <sys/ioctl.h> | |
45 | #include <sys/kinfo.h> | |
46 | #include <sys/tty.h> | |
8387d405 DG |
47 | #include <sys/file.h> |
48 | #include <sys/types.h> | |
15637ed4 RG |
49 | #include <machine/vmparam.h> |
50 | #include <fcntl.h> | |
51 | #include <nlist.h> | |
52 | #include <kvm.h> | |
53 | #include <ndbm.h> | |
54 | #include <limits.h> | |
55 | #include <paths.h> | |
56 | #include <stdio.h> | |
57 | #include <string.h> | |
8387d405 | 58 | #include <sys/mman.h> |
15637ed4 | 59 | |
15637ed4 RG |
60 | #define btop(x) (((unsigned)(x)) >> PGSHIFT) /* XXX */ |
61 | #define ptob(x) ((caddr_t)((x) << PGSHIFT)) /* XXX */ | |
62 | #include <vm/vm.h> /* ??? kinfo_proc currently includes this*/ | |
63 | #include <vm/vm_page.h> | |
64 | #include <vm/swap_pager.h> | |
65 | #include <sys/kinfo_proc.h> | |
15637ed4 RG |
66 | |
67 | /* | |
68 | * files | |
69 | */ | |
70 | static const char *unixf, *memf, *kmemf, *swapf; | |
71 | static int unixx, mem, kmem, swap; | |
72 | static DBM *db; | |
73 | /* | |
74 | * flags | |
75 | */ | |
76 | static int deadkernel; | |
77 | static int kvminit = 0; | |
78 | static int kvmfilesopen = 0; | |
79 | /* | |
80 | * state | |
81 | */ | |
82 | static struct kinfo_proc *kvmprocbase, *kvmprocptr; | |
83 | static int kvmnprocs; | |
84 | /* | |
85 | * u. buffer | |
86 | */ | |
87 | static union { | |
88 | struct user user; | |
89 | char upages[UPAGES][NBPG]; | |
90 | } user; | |
91 | ||
15637ed4 RG |
92 | struct swapblk { |
93 | long offset; /* offset in swap device */ | |
94 | long size; /* remaining size of block in swap device */ | |
95 | }; | |
15637ed4 RG |
96 | /* |
97 | * random other stuff | |
98 | */ | |
15637ed4 RG |
99 | static int dmmin, dmmax; |
100 | static int pcbpf; | |
101 | static int argaddr0; /* XXX */ | |
102 | static int argaddr1; | |
103 | static int swaddr; | |
104 | static int nswap; | |
105 | static char *tmp; | |
15637ed4 | 106 | static struct pde *PTD; |
15637ed4 RG |
107 | |
108 | #define basename(cp) ((tmp=rindex((cp), '/')) ? tmp+1 : (cp)) | |
109 | #define MAXSYMSIZE 256 | |
110 | ||
15637ed4 RG |
111 | #ifndef pftoc |
112 | #define pftoc(f) (f) | |
113 | #endif | |
114 | #ifndef iskva | |
115 | #define iskva(v) ((u_long)(v) & KERNBASE) | |
116 | #endif | |
117 | ||
118 | static struct nlist nl[] = { | |
119 | { "_Usrptmap" }, | |
120 | #define X_USRPTMAP 0 | |
121 | { "_usrpt" }, | |
122 | #define X_USRPT 1 | |
123 | { "_nswap" }, | |
124 | #define X_NSWAP 2 | |
125 | { "_dmmin" }, | |
126 | #define X_DMMIN 3 | |
127 | { "_dmmax" }, | |
128 | #define X_DMMAX 4 | |
129 | { "_vm_page_buckets" }, | |
130 | #define X_VM_PAGE_BUCKETS 5 | |
131 | { "_vm_page_hash_mask" }, | |
132 | #define X_VM_PAGE_HASH_MASK 6 | |
133 | { "_page_shift" }, | |
134 | #define X_PAGE_SHIFT 7 | |
8387d405 DG |
135 | { "_kstack" }, |
136 | #define X_KSTACK 8 | |
137 | { "_kernel_object" }, | |
138 | #define X_KERNEL_OBJECT 9 | |
139 | { "_btext",}, | |
140 | #define X_KERNEL_BTEXT 10 | |
15637ed4 RG |
141 | /* |
142 | * everything here and down, only if a dead kernel | |
143 | */ | |
144 | { "_Sysmap" }, | |
8387d405 | 145 | #define X_SYSMAP 11 |
15637ed4 RG |
146 | #define X_DEADKERNEL X_SYSMAP |
147 | { "_Syssize" }, | |
8387d405 | 148 | #define X_SYSSIZE 12 |
15637ed4 | 149 | { "_allproc" }, |
8387d405 | 150 | #define X_ALLPROC 13 |
15637ed4 | 151 | { "_zombproc" }, |
8387d405 | 152 | #define X_ZOMBPROC 14 |
15637ed4 | 153 | { "_nproc" }, |
8387d405 DG |
154 | #define X_NPROC 15 |
155 | #define X_LAST 15 | |
15637ed4 RG |
156 | { "_IdlePTD" }, |
157 | #define X_IdlePTD (X_LAST+1) | |
15637ed4 RG |
158 | { "" }, |
159 | }; | |
160 | ||
62f52313 DG |
161 | static off_t Vtophys(); |
162 | static void klseek(), seterr(), setsyserr(), vstodb(); | |
15637ed4 | 163 | static int getkvars(), kvm_doprocs(), kvm_init(); |
62f52313 | 164 | static int findpage(); |
15637ed4 RG |
165 | |
166 | /* | |
167 | * returns 0 if files were opened now, | |
168 | * 1 if files were already opened, | |
169 | * -1 if files could not be opened. | |
170 | */ | |
171 | kvm_openfiles(uf, mf, sf) | |
172 | const char *uf, *mf, *sf; | |
173 | { | |
174 | if (kvmfilesopen) | |
175 | return (1); | |
176 | unixx = mem = kmem = swap = -1; | |
177 | unixf = (uf == NULL) ? _PATH_UNIX : uf; | |
178 | memf = (mf == NULL) ? _PATH_MEM : mf; | |
179 | ||
180 | if ((unixx = open(unixf, O_RDONLY, 0)) == -1) { | |
181 | setsyserr("can't open %s", unixf); | |
182 | goto failed; | |
183 | } | |
184 | if ((mem = open(memf, O_RDONLY, 0)) == -1) { | |
185 | setsyserr("can't open %s", memf); | |
186 | goto failed; | |
187 | } | |
188 | if (sf != NULL) | |
189 | swapf = sf; | |
190 | if (mf != NULL) { | |
191 | deadkernel++; | |
192 | kmemf = mf; | |
193 | kmem = mem; | |
194 | swap = -1; | |
195 | } else { | |
196 | kmemf = _PATH_KMEM; | |
197 | if ((kmem = open(kmemf, O_RDONLY, 0)) == -1) { | |
198 | setsyserr("can't open %s", kmemf); | |
199 | goto failed; | |
200 | } | |
201 | swapf = (sf == NULL) ? _PATH_DRUM : sf; | |
202 | /* | |
203 | * live kernel - avoid looking up nlist entries | |
204 | * past X_DEADKERNEL. | |
205 | */ | |
206 | nl[X_DEADKERNEL].n_name = ""; | |
207 | } | |
208 | if (swapf != NULL && ((swap = open(swapf, O_RDONLY, 0)) == -1)) { | |
209 | seterr("can't open %s", swapf); | |
210 | goto failed; | |
211 | } | |
212 | kvmfilesopen++; | |
213 | if (kvminit == 0 && kvm_init(NULL, NULL, NULL, 0) == -1) /*XXX*/ | |
214 | return (-1); | |
215 | return (0); | |
216 | failed: | |
217 | kvm_close(); | |
218 | return (-1); | |
219 | } | |
220 | ||
221 | static | |
222 | kvm_init(uf, mf, sf) | |
223 | char *uf, *mf, *sf; | |
224 | { | |
225 | if (kvmfilesopen == 0 && kvm_openfiles(NULL, NULL, NULL) == -1) | |
226 | return (-1); | |
227 | if (getkvars() == -1) | |
228 | return (-1); | |
229 | kvminit = 1; | |
230 | ||
231 | return (0); | |
232 | } | |
233 | ||
234 | kvm_close() | |
235 | { | |
236 | if (unixx != -1) { | |
237 | close(unixx); | |
238 | unixx = -1; | |
239 | } | |
240 | if (kmem != -1) { | |
241 | if (kmem != mem) | |
242 | close(kmem); | |
243 | /* otherwise kmem is a copy of mem, and will be closed below */ | |
244 | kmem = -1; | |
245 | } | |
246 | if (mem != -1) { | |
247 | close(mem); | |
248 | mem = -1; | |
249 | } | |
250 | if (swap != -1) { | |
251 | close(swap); | |
252 | swap = -1; | |
253 | } | |
254 | if (db != NULL) { | |
255 | dbm_close(db); | |
256 | db = NULL; | |
257 | } | |
258 | kvminit = 0; | |
259 | kvmfilesopen = 0; | |
260 | deadkernel = 0; | |
15637ed4 RG |
261 | } |
262 | ||
263 | kvm_nlist(nl) | |
264 | struct nlist *nl; | |
265 | { | |
266 | datum key, data; | |
267 | char dbname[MAXPATHLEN]; | |
268 | char dbversion[_POSIX2_LINE_MAX]; | |
269 | char kversion[_POSIX2_LINE_MAX]; | |
270 | int dbversionlen; | |
271 | char symbuf[MAXSYMSIZE]; | |
272 | struct nlist nbuf, *n; | |
273 | int num, did; | |
274 | ||
275 | if (kvmfilesopen == 0 && kvm_openfiles(NULL, NULL, NULL) == -1) | |
276 | return (-1); | |
277 | if (deadkernel) | |
278 | goto hard2; | |
279 | /* | |
280 | * initialize key datum | |
281 | */ | |
282 | key.dptr = symbuf; | |
283 | ||
284 | if (db != NULL) | |
285 | goto win; /* off to the races */ | |
286 | /* | |
287 | * open database | |
288 | */ | |
289 | sprintf(dbname, "%s/kvm_%s", _PATH_VARRUN, basename(unixf)); | |
290 | if ((db = dbm_open(dbname, O_RDONLY, 0)) == NULL) | |
291 | goto hard2; | |
292 | /* | |
293 | * read version out of database | |
294 | */ | |
295 | bcopy("VERSION", symbuf, sizeof ("VERSION")-1); | |
296 | key.dsize = (sizeof ("VERSION") - 1); | |
297 | data = dbm_fetch(db, key); | |
298 | if (data.dptr == NULL) | |
299 | goto hard1; | |
300 | bcopy(data.dptr, dbversion, data.dsize); | |
301 | dbversionlen = data.dsize; | |
302 | /* | |
303 | * read version string from kernel memory | |
304 | */ | |
305 | bcopy("_version", symbuf, sizeof ("_version")-1); | |
306 | key.dsize = (sizeof ("_version")-1); | |
307 | data = dbm_fetch(db, key); | |
308 | if (data.dptr == NULL) | |
309 | goto hard1; | |
310 | if (data.dsize != sizeof (struct nlist)) | |
311 | goto hard1; | |
312 | bcopy(data.dptr, &nbuf, sizeof (struct nlist)); | |
313 | lseek(kmem, nbuf.n_value, 0); | |
314 | if (read(kmem, kversion, dbversionlen) != dbversionlen) | |
315 | goto hard1; | |
316 | /* | |
317 | * if they match, we win - otherwise do it the hard way | |
318 | */ | |
319 | if (bcmp(dbversion, kversion, dbversionlen) != 0) | |
320 | goto hard1; | |
321 | /* | |
322 | * getem from the database. | |
323 | */ | |
324 | win: | |
325 | num = did = 0; | |
326 | for (n = nl; n->n_name && n->n_name[0]; n++, num++) { | |
327 | int len; | |
328 | /* | |
329 | * clear out fields from users buffer | |
330 | */ | |
331 | n->n_type = 0; | |
332 | n->n_other = 0; | |
333 | n->n_desc = 0; | |
334 | n->n_value = 0; | |
335 | /* | |
336 | * query db | |
337 | */ | |
338 | if ((len = strlen(n->n_name)) > MAXSYMSIZE) { | |
339 | seterr("symbol too large"); | |
340 | return (-1); | |
341 | } | |
342 | (void)strcpy(symbuf, n->n_name); | |
343 | key.dsize = len; | |
344 | data = dbm_fetch(db, key); | |
345 | if (data.dptr == NULL || data.dsize != sizeof (struct nlist)) | |
346 | continue; | |
347 | bcopy(data.dptr, &nbuf, sizeof (struct nlist)); | |
348 | n->n_value = nbuf.n_value; | |
349 | n->n_type = nbuf.n_type; | |
350 | n->n_desc = nbuf.n_desc; | |
351 | n->n_other = nbuf.n_other; | |
352 | did++; | |
353 | } | |
354 | return (num - did); | |
355 | hard1: | |
356 | dbm_close(db); | |
357 | db = NULL; | |
358 | hard2: | |
359 | num = nlist(unixf, nl); | |
360 | if (num == -1) | |
361 | seterr("nlist (hard way) failed"); | |
362 | return (num); | |
363 | } | |
364 | ||
365 | kvm_getprocs(what, arg) | |
366 | int what, arg; | |
367 | { | |
368 | static int ocopysize = -1; | |
369 | ||
370 | if (kvminit == 0 && kvm_init(NULL, NULL, NULL, 0) == -1) | |
371 | return (NULL); | |
372 | if (!deadkernel) { | |
373 | int ret, copysize; | |
374 | ||
375 | if ((ret = getkerninfo(what, NULL, NULL, arg)) == -1) { | |
376 | setsyserr("can't get estimate for kerninfo"); | |
377 | return (-1); | |
378 | } | |
379 | copysize = ret; | |
20cc2df0 DG |
380 | if ((copysize > ocopysize || |
381 | kvmprocbase == (struct kinfo_proc *) NULL) && | |
170dc86a DG |
382 | (kvmprocbase = (struct kinfo_proc *) |
383 | realloc(kvmprocbase, copysize)) == NULL) { | |
15637ed4 RG |
384 | seterr("out of memory"); |
385 | return (-1); | |
386 | } | |
387 | ocopysize = copysize; | |
388 | if ((ret = getkerninfo(what, kvmprocbase, ©size, | |
389 | arg)) == -1) { | |
390 | setsyserr("can't get proc list"); | |
391 | return (-1); | |
392 | } | |
393 | if (copysize % sizeof (struct kinfo_proc)) { | |
394 | seterr("proc size mismatch (got %d total, kinfo_proc: %d)", | |
395 | copysize, sizeof (struct kinfo_proc)); | |
396 | return (-1); | |
397 | } | |
398 | kvmnprocs = copysize / sizeof (struct kinfo_proc); | |
399 | } else { | |
400 | int nproc; | |
401 | ||
402 | if (kvm_read((void *) nl[X_NPROC].n_value, &nproc, | |
403 | sizeof (int)) != sizeof (int)) { | |
404 | seterr("can't read nproc"); | |
405 | return (-1); | |
406 | } | |
407 | if ((kvmprocbase = (struct kinfo_proc *) | |
408 | malloc(nproc * sizeof (struct kinfo_proc))) == NULL) { | |
409 | seterr("out of memory (addr: %x nproc = %d)", | |
410 | nl[X_NPROC].n_value, nproc); | |
411 | return (-1); | |
412 | } | |
413 | kvmnprocs = kvm_doprocs(what, arg, kvmprocbase); | |
414 | realloc(kvmprocbase, kvmnprocs * sizeof (struct kinfo_proc)); | |
415 | } | |
416 | kvmprocptr = kvmprocbase; | |
417 | ||
418 | return (kvmnprocs); | |
419 | } | |
420 | ||
421 | /* | |
422 | * XXX - should NOT give up so easily - especially since the kernel | |
423 | * may be corrupt (it died). Should gather as much information as possible. | |
424 | * Follows proc ptrs instead of reading table since table may go | |
425 | * away soon. | |
426 | */ | |
427 | static | |
428 | kvm_doprocs(what, arg, buff) | |
429 | int what, arg; | |
430 | char *buff; | |
431 | { | |
432 | struct proc *p, proc; | |
433 | register char *bp = buff; | |
434 | int i = 0; | |
435 | int doingzomb = 0; | |
436 | struct eproc eproc; | |
437 | struct pgrp pgrp; | |
438 | struct session sess; | |
439 | struct tty tty; | |
15637ed4 RG |
440 | |
441 | /* allproc */ | |
442 | if (kvm_read((void *) nl[X_ALLPROC].n_value, &p, | |
443 | sizeof (struct proc *)) != sizeof (struct proc *)) { | |
444 | seterr("can't read allproc"); | |
445 | return (-1); | |
446 | } | |
447 | ||
448 | again: | |
449 | for (; p; p = proc.p_nxt) { | |
450 | if (kvm_read(p, &proc, sizeof (struct proc)) != | |
451 | sizeof (struct proc)) { | |
452 | seterr("can't read proc at %x", p); | |
453 | return (-1); | |
454 | } | |
15637ed4 RG |
455 | if (kvm_read(proc.p_cred, &eproc.e_pcred, |
456 | sizeof (struct pcred)) == sizeof (struct pcred)) | |
457 | (void) kvm_read(eproc.e_pcred.pc_ucred, &eproc.e_ucred, | |
458 | sizeof (struct ucred)); | |
459 | switch(ki_op(what)) { | |
460 | ||
461 | case KINFO_PROC_PID: | |
462 | if (proc.p_pid != (pid_t)arg) | |
463 | continue; | |
464 | break; | |
465 | ||
466 | ||
467 | case KINFO_PROC_UID: | |
468 | if (eproc.e_ucred.cr_uid != (uid_t)arg) | |
469 | continue; | |
470 | break; | |
471 | ||
472 | case KINFO_PROC_RUID: | |
473 | if (eproc.e_pcred.p_ruid != (uid_t)arg) | |
474 | continue; | |
475 | break; | |
476 | } | |
15637ed4 RG |
477 | /* |
478 | * gather eproc | |
479 | */ | |
480 | eproc.e_paddr = p; | |
481 | if (kvm_read(proc.p_pgrp, &pgrp, sizeof (struct pgrp)) != | |
482 | sizeof (struct pgrp)) { | |
483 | seterr("can't read pgrp at %x", proc.p_pgrp); | |
484 | return (-1); | |
485 | } | |
486 | eproc.e_sess = pgrp.pg_session; | |
487 | eproc.e_pgid = pgrp.pg_id; | |
488 | eproc.e_jobc = pgrp.pg_jobc; | |
489 | if (kvm_read(pgrp.pg_session, &sess, sizeof (struct session)) | |
490 | != sizeof (struct session)) { | |
491 | seterr("can't read session at %x", pgrp.pg_session); | |
492 | return (-1); | |
493 | } | |
494 | if ((proc.p_flag&SCTTY) && sess.s_ttyp != NULL) { | |
495 | if (kvm_read(sess.s_ttyp, &tty, sizeof (struct tty)) | |
496 | != sizeof (struct tty)) { | |
497 | seterr("can't read tty at %x", sess.s_ttyp); | |
498 | return (-1); | |
499 | } | |
500 | eproc.e_tdev = tty.t_dev; | |
501 | eproc.e_tsess = tty.t_session; | |
502 | if (tty.t_pgrp != NULL) { | |
503 | if (kvm_read(tty.t_pgrp, &pgrp, sizeof (struct | |
504 | pgrp)) != sizeof (struct pgrp)) { | |
505 | seterr("can't read tpgrp at &x", | |
506 | tty.t_pgrp); | |
507 | return (-1); | |
508 | } | |
509 | eproc.e_tpgid = pgrp.pg_id; | |
510 | } else | |
511 | eproc.e_tpgid = -1; | |
512 | } else | |
513 | eproc.e_tdev = NODEV; | |
514 | if (proc.p_wmesg) | |
515 | kvm_read(proc.p_wmesg, eproc.e_wmesg, WMESGLEN); | |
15637ed4 RG |
516 | (void) kvm_read(proc.p_vmspace, &eproc.e_vm, |
517 | sizeof (struct vmspace)); | |
518 | eproc.e_xsize = eproc.e_xrssize = | |
519 | eproc.e_xccount = eproc.e_xswrss = 0; | |
15637ed4 RG |
520 | |
521 | switch(ki_op(what)) { | |
522 | ||
523 | case KINFO_PROC_PGRP: | |
524 | if (eproc.e_pgid != (pid_t)arg) | |
525 | continue; | |
526 | break; | |
527 | ||
528 | case KINFO_PROC_TTY: | |
529 | if ((proc.p_flag&SCTTY) == 0 || | |
530 | eproc.e_tdev != (dev_t)arg) | |
531 | continue; | |
532 | break; | |
533 | } | |
534 | ||
535 | i++; | |
536 | bcopy(&proc, bp, sizeof (struct proc)); | |
537 | bp += sizeof (struct proc); | |
538 | bcopy(&eproc, bp, sizeof (struct eproc)); | |
539 | bp+= sizeof (struct eproc); | |
540 | } | |
541 | if (!doingzomb) { | |
542 | /* zombproc */ | |
543 | if (kvm_read((void *) nl[X_ZOMBPROC].n_value, &p, | |
544 | sizeof (struct proc *)) != sizeof (struct proc *)) { | |
545 | seterr("can't read zombproc"); | |
546 | return (-1); | |
547 | } | |
548 | doingzomb = 1; | |
549 | goto again; | |
550 | } | |
551 | ||
552 | return (i); | |
553 | } | |
554 | ||
555 | struct proc * | |
556 | kvm_nextproc() | |
557 | { | |
558 | ||
559 | if (!kvmprocbase && kvm_getprocs(0, 0) == -1) | |
560 | return (NULL); | |
561 | if (kvmprocptr >= (kvmprocbase + kvmnprocs)) { | |
8387d405 | 562 | #if 0 |
15637ed4 | 563 | seterr("end of proc list"); |
8387d405 | 564 | #endif |
15637ed4 RG |
565 | return (NULL); |
566 | } | |
567 | return((struct proc *)(kvmprocptr++)); | |
568 | } | |
569 | ||
570 | struct eproc * | |
571 | kvm_geteproc(p) | |
572 | const struct proc *p; | |
573 | { | |
574 | return ((struct eproc *)(((char *)p) + sizeof (struct proc))); | |
575 | } | |
576 | ||
577 | kvm_setproc() | |
578 | { | |
579 | kvmprocptr = kvmprocbase; | |
580 | } | |
581 | ||
582 | kvm_freeprocs() | |
583 | { | |
584 | ||
585 | if (kvmprocbase) { | |
586 | free(kvmprocbase); | |
587 | kvmprocbase = NULL; | |
588 | } | |
589 | } | |
590 | ||
8387d405 DG |
591 | proc_getmem(const struct proc *p, void *buffer, vm_offset_t size, vm_offset_t offset) { |
592 | int fd; | |
593 | char fn[512+1]; | |
594 | sprintf(fn,"/proc/%d",p->p_pid); | |
62f52313 | 595 | if (p->p_flag & SSYS) |
8387d405 DG |
596 | return 0; |
597 | fd = open(fn,O_RDONLY); | |
62f52313 | 598 | if (fd == -1) { |
8387d405 DG |
599 | return 0; |
600 | } | |
601 | ||
62f52313 | 602 | if (lseek(fd, offset, 0) == -1) { |
8387d405 DG |
603 | close(fd); |
604 | return 0; | |
605 | } | |
62f52313 | 606 | if (read(fd, buffer, size) <= 0) { |
8387d405 DG |
607 | close(fd); |
608 | return 0; | |
609 | } | |
610 | close(fd); | |
611 | return 1; | |
612 | } | |
15637ed4 | 613 | |
15637ed4 RG |
614 | struct user * |
615 | kvm_getu(p) | |
616 | const struct proc *p; | |
617 | { | |
618 | register struct kinfo_proc *kp = (struct kinfo_proc *)p; | |
619 | register int i; | |
620 | register char *up; | |
621 | u_int vaddr; | |
8387d405 | 622 | int arg_size; |
15637ed4 RG |
623 | |
624 | if (kvminit == 0 && kvm_init(NULL, NULL, NULL, 0) == -1) | |
625 | return (NULL); | |
626 | if (p->p_stat == SZOMB) { | |
627 | seterr("zombie process"); | |
628 | return (NULL); | |
629 | } | |
630 | ||
62f52313 DG |
631 | if (!deadkernel) { |
632 | if (proc_getmem(p, user.upages, sizeof user.upages, USRSTACK)) { | |
633 | kp->kp_eproc.e_vm.vm_rssize = | |
634 | kp->kp_eproc.e_vm.vm_pmap.pm_stats.resident_count; /* XXX */ | |
635 | return &user.user; | |
636 | } | |
8387d405 DG |
637 | } |
638 | ||
62f52313 DG |
639 | argaddr0 = argaddr1 = 0; |
640 | /* | |
641 | * Read u-area one page at a time for the benefit of post-mortems | |
642 | */ | |
643 | up = (char *) p->p_addr; | |
644 | for (i = 0; i < UPAGES; i++) { | |
645 | klseek(kmem, (long)up, 0); | |
646 | if (read(kmem, user.upages[i], CLBYTES) != CLBYTES) { | |
647 | seterr("cant read page %x of u of pid %d from %s", | |
648 | up, p->p_pid, kmemf); | |
649 | return(NULL); | |
650 | } | |
651 | up += CLBYTES; | |
652 | } | |
653 | ||
654 | pcbpf = (int) btop(p->p_addr); /* what should this be really? */ | |
655 | /* | |
656 | * Conjure up a physical address for the arguments. | |
657 | */ | |
658 | kp->kp_eproc.e_vm.vm_rssize = | |
659 | kp->kp_eproc.e_vm.vm_pmap.pm_stats.resident_count; /* XXX */ | |
660 | ||
661 | vaddr = (u_int)kp->kp_eproc.e_vm.vm_minsaddr; | |
662 | arg_size = USRSTACK - vaddr; | |
663 | ||
664 | if (kp->kp_eproc.e_vm.vm_pmap.pm_pdir) { | |
665 | struct pde pde; | |
666 | ||
667 | klseek(kmem, | |
668 | (long)(&kp->kp_eproc.e_vm.vm_pmap.pm_pdir[pdei(vaddr)]), 0); | |
669 | ||
670 | if (read(kmem, (char *)&pde, sizeof pde) == sizeof pde | |
671 | && pde.pd_v) { | |
672 | ||
673 | struct pte pte; | |
674 | ||
675 | if (lseek(mem, (long)ctob(pde.pd_pfnum) + | |
676 | (ptei(vaddr) * sizeof pte), 0) == -1) | |
677 | seterr("kvm_getu: lseek"); | |
678 | if (read(mem, (char *)&pte, sizeof pte) == sizeof pte) { | |
679 | if (pte.pg_v) { | |
680 | argaddr1 = (pte.pg_pfnum << PGSHIFT) | | |
681 | ((u_long)vaddr & (NBPG-1)); | |
682 | } | |
683 | } else { | |
684 | seterr("kvm_getu: read"); | |
685 | } | |
686 | } | |
687 | } | |
688 | ||
689 | return(&user.user); | |
15637ed4 | 690 | } |
15637ed4 RG |
691 | |
692 | char * | |
693 | kvm_getargs(p, up) | |
694 | const struct proc *p; | |
695 | const struct user *up; | |
696 | { | |
8387d405 DG |
697 | int arg_size, arg_offset; |
698 | static char cmdbuf[ARG_MAX]; | |
699 | char argc[ARG_MAX*3]; | |
700 | int *argv; | |
15637ed4 | 701 | register char *cp; |
15637ed4 RG |
702 | char c; |
703 | int nbad; | |
8387d405 DG |
704 | char *cmdbufp; |
705 | vm_offset_t vaddr; | |
706 | char procfile[16]; | |
707 | int mmfd; | |
708 | #if 0 | |
709 | char *argc = NULL; | |
15637ed4 | 710 | #endif |
15637ed4 | 711 | |
8387d405 DG |
712 | *cmdbuf = 0; |
713 | ||
714 | vaddr = (u_int)((struct kinfo_proc *)p)->kp_eproc.e_vm.vm_minsaddr; | |
715 | arg_size = USRSTACK - vaddr; | |
716 | ||
717 | if (arg_size >= 3*ARG_MAX) | |
718 | goto bad; | |
719 | ||
720 | #if 0 | |
721 | sprintf(procfile, "/proc/%d", p->p_pid); | |
722 | if ((mmfd = open(procfile, O_RDONLY, 0)) == -1) { | |
723 | printf("failed to open %s\n",procfile); | |
724 | goto bad; | |
725 | } | |
726 | ||
727 | if ((argc = mmap(0, arg_size, PROT_READ, MAP_FILE, mmfd, vaddr)) | |
728 | == (char *)-1) { | |
729 | printf("failed to mmap %s error=%s\n", procfile, strerror(errno)); | |
730 | goto bad; | |
731 | } | |
15637ed4 | 732 | #endif |
8387d405 | 733 | |
62f52313 DG |
734 | if (!proc_getmem(p, argc, arg_size, vaddr)) { |
735 | if ((p->p_flag & SLOAD) == 0 || argaddr1 == 0) { | |
736 | goto bad; | |
737 | } else { | |
738 | lseek(mem, (long)argaddr1, 0); | |
739 | if (read(mem, argc, arg_size) != arg_size) | |
740 | goto bad; | |
741 | } | |
742 | } | |
15637ed4 | 743 | |
8387d405 DG |
744 | argv = (int *)argc; |
745 | ||
746 | arg_offset = argv[0] - vaddr; | |
747 | if (arg_offset >= 3*ARG_MAX) | |
748 | goto bad; | |
749 | ||
15637ed4 | 750 | nbad = 0; |
15637ed4 | 751 | |
8387d405 DG |
752 | cmdbufp = cmdbuf; |
753 | for (cp = &argc[arg_offset]; cp < &argc[arg_size]; cp++, cmdbufp++) { | |
754 | c = *cmdbufp = *cp; | |
15637ed4 | 755 | if (c == 0) { /* convert null between arguments to space */ |
8387d405 | 756 | *cmdbufp = ' '; |
15637ed4 RG |
757 | if (*(cp+1) == 0) break; /* if null argument follows then no more args */ |
758 | } | |
759 | else if (c < ' ' || c > 0176) { | |
760 | if (++nbad >= 5*(0+1)) { /* eflg -> 0 XXX */ /* limit number of bad chars to 5 */ | |
8387d405 | 761 | *cmdbufp++ = '?'; |
15637ed4 RG |
762 | break; |
763 | } | |
8387d405 | 764 | *cmdbufp = '?'; |
15637ed4 RG |
765 | } |
766 | else if (0 == 0 && c == '=') { /* eflg -> 0 XXX */ | |
8387d405 DG |
767 | while (*--cmdbufp != ' ') |
768 | if (cmdbufp <= cmdbuf) | |
15637ed4 RG |
769 | break; |
770 | break; | |
771 | } | |
772 | } | |
8387d405 DG |
773 | *cmdbufp = 0; |
774 | ||
775 | while (*--cmdbufp == ' ') | |
776 | *cmdbufp = 0; | |
777 | ||
778 | if (cmdbuf[0] == '-' || cmdbuf[0] == '?' || cmdbuf[0] <= ' ') { | |
779 | bad: | |
15637ed4 RG |
780 | (void) strcat(cmdbuf, " ("); |
781 | (void) strncat(cmdbuf, p->p_comm, sizeof(p->p_comm)); | |
782 | (void) strcat(cmdbuf, ")"); | |
783 | } | |
8387d405 DG |
784 | #if 0 |
785 | if (argc && argc != (char *)-1) | |
786 | munmap(argc, arg_size); | |
787 | if (mmfd && mmfd != -1) | |
788 | close (mmfd); | |
789 | #endif | |
15637ed4 RG |
790 | return (cmdbuf); |
791 | } | |
792 | ||
62f52313 | 793 | |
15637ed4 RG |
794 | static |
795 | getkvars() | |
796 | { | |
797 | if (kvm_nlist(nl) == -1) | |
798 | return (-1); | |
62f52313 DG |
799 | if (deadkernel) { |
800 | /* We must do the sys map first because klseek uses it */ | |
801 | long addr; | |
802 | ||
803 | PTD = (struct pde *) malloc(NBPG); | |
804 | if (PTD == NULL) { | |
805 | seterr("out of space for PTD"); | |
806 | return (-1); | |
807 | } | |
808 | addr = (long) nl[X_IdlePTD].n_value; | |
809 | (void) lseek(kmem, addr, 0); | |
810 | read(kmem, (char *)&addr, sizeof(addr)); | |
811 | (void) lseek(kmem, (long)addr, 0); | |
812 | if (read(kmem, (char *) PTD, NBPG) != NBPG) { | |
813 | seterr("can't read PTD"); | |
814 | return (-1); | |
815 | } | |
816 | } | |
15637ed4 RG |
817 | if (kvm_read((void *) nl[X_NSWAP].n_value, &nswap, sizeof (long)) != |
818 | sizeof (long)) { | |
819 | seterr("can't read nswap"); | |
820 | return (-1); | |
821 | } | |
822 | if (kvm_read((void *) nl[X_DMMIN].n_value, &dmmin, sizeof (long)) != | |
823 | sizeof (long)) { | |
824 | seterr("can't read dmmin"); | |
825 | return (-1); | |
826 | } | |
827 | if (kvm_read((void *) nl[X_DMMAX].n_value, &dmmax, sizeof (long)) != | |
828 | sizeof (long)) { | |
829 | seterr("can't read dmmax"); | |
830 | return (-1); | |
831 | } | |
832 | return (0); | |
833 | } | |
834 | ||
835 | kvm_read(loc, buf, len) | |
836 | void *loc; | |
837 | void *buf; | |
838 | { | |
839 | if (kvmfilesopen == 0 && kvm_openfiles(NULL, NULL, NULL) == -1) | |
840 | return (-1); | |
841 | if (iskva(loc)) { | |
62f52313 | 842 | klseek(kmem, (off_t) loc, 0); |
15637ed4 RG |
843 | if (read(kmem, buf, len) != len) { |
844 | seterr("error reading kmem at %x", loc); | |
845 | return (-1); | |
846 | } | |
847 | } else { | |
848 | lseek(mem, (off_t) loc, 0); | |
849 | if (read(mem, buf, len) != len) { | |
850 | seterr("error reading mem at %x", loc); | |
851 | return (-1); | |
852 | } | |
853 | } | |
854 | return (len); | |
855 | } | |
856 | ||
62f52313 DG |
857 | static void |
858 | klseek(fd, loc, off) | |
859 | int fd; | |
860 | off_t loc; | |
861 | int off; | |
862 | { | |
863 | ||
864 | if (deadkernel) { | |
865 | if ((loc = Vtophys(loc)) == -1) | |
866 | return; | |
867 | } | |
868 | (void) lseek(fd, (off_t)loc, off); | |
869 | } | |
870 | ||
871 | static off_t | |
872 | Vtophys(loc) | |
873 | u_long loc; | |
874 | { | |
875 | off_t newloc = (off_t) -1; | |
876 | struct pde pde; | |
877 | struct pte pte; | |
878 | int p; | |
879 | ||
880 | pde = PTD[loc >> PD_SHIFT]; | |
881 | if (pde.pd_v == 0) { | |
882 | seterr("vtophys: page directory entry not valid"); | |
883 | return((off_t) -1); | |
884 | } | |
885 | p = btop(loc & PT_MASK); | |
886 | newloc = pde.pd_pfnum + (p * sizeof(struct pte)); | |
887 | (void) lseek(kmem, (long)newloc, 0); | |
888 | if (read(kmem, (char *)&pte, sizeof pte) != sizeof pte) { | |
889 | seterr("vtophys: cannot obtain desired pte"); | |
890 | return((off_t) -1); | |
891 | } | |
892 | newloc = pte.pg_pfnum; | |
893 | if (pte.pg_v == 0) { | |
894 | seterr("vtophys: page table entry not valid"); | |
895 | return((off_t) -1); | |
896 | } | |
897 | newloc += (loc & PGOFSET); | |
898 | return((off_t) newloc); | |
899 | } | |
900 | ||
901 | /* | |
902 | * locate address of unwired or swapped page | |
903 | */ | |
904 | ||
905 | ||
906 | #define KREAD(off, addr, len) \ | |
907 | (kvm_read((void *)(off), (char *)(addr), (len)) == (len)) | |
908 | ||
909 | #define atop(x) (((unsigned)(x)) >> page_shift) | |
910 | #define vm_page_hash(object, offset) \ | |
911 | (((unsigned)object+(unsigned)atop(offset))&vm_page_hash_mask) | |
912 | ||
913 | static int | |
914 | findpage(object, offset, maddr) | |
915 | long object; | |
916 | long offset; | |
917 | vm_offset_t *maddr; | |
918 | { | |
919 | static long vm_page_hash_mask; | |
920 | static long vm_page_buckets; | |
921 | static long page_shift; | |
922 | queue_head_t bucket; | |
923 | struct vm_page mem; | |
924 | long addr, baddr; | |
925 | ||
926 | if (vm_page_hash_mask == 0 && !KREAD(nl[X_VM_PAGE_HASH_MASK].n_value, | |
927 | &vm_page_hash_mask, sizeof (long))) { | |
928 | seterr("can't read vm_page_hash_mask"); | |
929 | return 0; | |
930 | } | |
931 | if (page_shift == 0 && !KREAD(nl[X_PAGE_SHIFT].n_value, | |
932 | &page_shift, sizeof (long))) { | |
933 | seterr("can't read page_shift"); | |
934 | return 0; | |
935 | } | |
936 | if (vm_page_buckets == 0 && !KREAD(nl[X_VM_PAGE_BUCKETS].n_value, | |
937 | &vm_page_buckets, sizeof (long))) { | |
938 | seterr("can't read vm_page_buckets"); | |
939 | return 0; | |
940 | } | |
941 | ||
942 | baddr = vm_page_buckets + vm_page_hash(object,offset) * sizeof(queue_head_t); | |
943 | if (!KREAD(baddr, &bucket, sizeof (bucket))) { | |
944 | seterr("can't read vm_page_bucket"); | |
945 | return 0; | |
946 | } | |
947 | ||
948 | addr = (long)bucket.next; | |
949 | while (addr != baddr) { | |
950 | if (!KREAD(addr, &mem, sizeof (mem))) { | |
951 | seterr("can't read vm_page"); | |
952 | return 0; | |
953 | } | |
954 | if ((long)mem.object == object && mem.offset == offset) { | |
955 | *maddr = (long)mem.phys_addr; | |
956 | return 1; | |
957 | } | |
958 | addr = (long)mem.hashq.next; | |
959 | } | |
960 | return 0; | |
961 | } | |
962 | ||
15637ed4 RG |
963 | #include <varargs.h> |
964 | static char errbuf[_POSIX2_LINE_MAX]; | |
965 | ||
966 | static void | |
967 | seterr(va_alist) | |
968 | va_dcl | |
969 | { | |
970 | char *fmt; | |
971 | va_list ap; | |
972 | ||
973 | va_start(ap); | |
974 | fmt = va_arg(ap, char *); | |
975 | (void) vsnprintf(errbuf, _POSIX2_LINE_MAX, fmt, ap); | |
976 | #if DEBUG | |
977 | (void) vfprintf(stderr, fmt, ap); | |
978 | #endif | |
979 | va_end(ap); | |
980 | } | |
981 | ||
982 | static void | |
983 | setsyserr(va_alist) | |
984 | va_dcl | |
985 | { | |
986 | char *fmt, *cp; | |
987 | va_list ap; | |
988 | extern int errno; | |
989 | ||
990 | va_start(ap); | |
991 | fmt = va_arg(ap, char *); | |
992 | (void) vsnprintf(errbuf, _POSIX2_LINE_MAX, fmt, ap); | |
993 | for (cp=errbuf; *cp; cp++) | |
994 | ; | |
995 | snprintf(cp, _POSIX2_LINE_MAX - (cp - errbuf), ": %s", strerror(errno)); | |
8387d405 DG |
996 | #if DEBUG |
997 | (void) fprintf(stderr, "%s\n", errbuf); | |
998 | #endif | |
15637ed4 RG |
999 | va_end(ap); |
1000 | } | |
1001 | ||
1002 | char * | |
1003 | kvm_geterr() | |
1004 | { | |
1005 | return (errbuf); | |
1006 | } |