xref: /illumos-gate/usr/src/lib/libkvm/common/kvm.c (revision 1320caf7cc74a3c5be65ef23516dee229adc288a)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
27*1320caf7SBryan Cantrill /*
28*1320caf7SBryan Cantrill  * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
29*1320caf7SBryan Cantrill  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <kvm.h>
327c478bd9Sstevel@tonic-gate #include <stdio.h>
337c478bd9Sstevel@tonic-gate #include <stdlib.h>
347c478bd9Sstevel@tonic-gate #include <stdarg.h>
357c478bd9Sstevel@tonic-gate #include <unistd.h>
367c478bd9Sstevel@tonic-gate #include <limits.h>
377c478bd9Sstevel@tonic-gate #include <fcntl.h>
387c478bd9Sstevel@tonic-gate #include <strings.h>
39*1320caf7SBryan Cantrill #include <errno.h>
407c478bd9Sstevel@tonic-gate #include <sys/mem.h>
417c478bd9Sstevel@tonic-gate #include <sys/stat.h>
427c478bd9Sstevel@tonic-gate #include <sys/mman.h>
437c478bd9Sstevel@tonic-gate #include <sys/dumphdr.h>
447c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate struct _kvmd {
477c478bd9Sstevel@tonic-gate 	struct dumphdr	kvm_dump;
487c478bd9Sstevel@tonic-gate 	char		*kvm_debug;
497c478bd9Sstevel@tonic-gate 	int		kvm_openflag;
507c478bd9Sstevel@tonic-gate 	int		kvm_corefd;
517c478bd9Sstevel@tonic-gate 	int		kvm_kmemfd;
527c478bd9Sstevel@tonic-gate 	int		kvm_memfd;
537c478bd9Sstevel@tonic-gate 	size_t		kvm_coremapsize;
547c478bd9Sstevel@tonic-gate 	char		*kvm_core;
557c478bd9Sstevel@tonic-gate 	dump_map_t	*kvm_map;
567c478bd9Sstevel@tonic-gate 	pfn_t		*kvm_pfn;
577c478bd9Sstevel@tonic-gate 	struct as	*kvm_kas;
587c478bd9Sstevel@tonic-gate 	proc_t		*kvm_practive;
597c478bd9Sstevel@tonic-gate 	pid_t		kvm_pid;
607c478bd9Sstevel@tonic-gate 	char		kvm_namelist[MAXNAMELEN + 1];
61*1320caf7SBryan Cantrill 	boolean_t	kvm_namelist_core;
627c478bd9Sstevel@tonic-gate 	proc_t		kvm_proc;
637c478bd9Sstevel@tonic-gate };
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate #define	PREAD	(ssize_t (*)(int, void *, size_t, offset_t))pread64
667c478bd9Sstevel@tonic-gate #define	PWRITE	(ssize_t (*)(int, void *, size_t, offset_t))pwrite64
677c478bd9Sstevel@tonic-gate 
68*1320caf7SBryan Cantrill static int kvm_nlist_core(kvm_t *kd, struct nlist nl[], const char *err);
69*1320caf7SBryan Cantrill 
707c478bd9Sstevel@tonic-gate static kvm_t *
717c478bd9Sstevel@tonic-gate fail(kvm_t *kd, const char *err, const char *message, ...)
727c478bd9Sstevel@tonic-gate {
737c478bd9Sstevel@tonic-gate 	va_list args;
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate 	va_start(args, message);
767c478bd9Sstevel@tonic-gate 	if (err || (kd && kd->kvm_debug)) {
777c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", err ? err : "KVM_DEBUG");
787c478bd9Sstevel@tonic-gate 		(void) vfprintf(stderr, message, args);
797c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "\n");
807c478bd9Sstevel@tonic-gate 	}
817c478bd9Sstevel@tonic-gate 	va_end(args);
827c478bd9Sstevel@tonic-gate 	if (kd != NULL)
837c478bd9Sstevel@tonic-gate 		(void) kvm_close(kd);
847c478bd9Sstevel@tonic-gate 	return (NULL);
857c478bd9Sstevel@tonic-gate }
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate /*ARGSUSED*/
887c478bd9Sstevel@tonic-gate kvm_t *
897c478bd9Sstevel@tonic-gate kvm_open(const char *namelist, const char *corefile, const char *swapfile,
907c478bd9Sstevel@tonic-gate 	int flag, const char *err)
917c478bd9Sstevel@tonic-gate {
927c478bd9Sstevel@tonic-gate 	kvm_t *kd;
937c478bd9Sstevel@tonic-gate 	struct stat64 memstat, kmemstat, allkmemstat, corestat;
947c478bd9Sstevel@tonic-gate 	struct nlist nl[3] = { { "kas" }, { "practive" }, { "" } };
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate 	if ((kd = calloc(1, sizeof (kvm_t))) == NULL)
977c478bd9Sstevel@tonic-gate 		return (fail(NULL, err, "cannot allocate space for kvm_t"));
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate 	kd->kvm_corefd = kd->kvm_kmemfd = kd->kvm_memfd = -1;
1007c478bd9Sstevel@tonic-gate 	kd->kvm_debug = getenv("KVM_DEBUG");
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate 	if ((kd->kvm_openflag = flag) != O_RDONLY && flag != O_RDWR)
1037c478bd9Sstevel@tonic-gate 		return (fail(kd, err, "illegal flag 0x%x to kvm_open()", flag));
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate 	if (corefile == NULL)
1067c478bd9Sstevel@tonic-gate 		corefile = "/dev/kmem";
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate 	if (stat64(corefile, &corestat) == -1)
1097c478bd9Sstevel@tonic-gate 		return (fail(kd, err, "cannot stat %s", corefile));
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 	if (S_ISCHR(corestat.st_mode)) {
1127c478bd9Sstevel@tonic-gate 		if (stat64("/dev/mem", &memstat) == -1)
1137c478bd9Sstevel@tonic-gate 			return (fail(kd, err, "cannot stat /dev/mem"));
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 		if (stat64("/dev/kmem", &kmemstat) == -1)
1167c478bd9Sstevel@tonic-gate 			return (fail(kd, err, "cannot stat /dev/kmem"));
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 		if (stat64("/dev/allkmem", &allkmemstat) == -1)
1197c478bd9Sstevel@tonic-gate 			return (fail(kd, err, "cannot stat /dev/allkmem"));
1207c478bd9Sstevel@tonic-gate 		if (corestat.st_rdev == memstat.st_rdev ||
1217c478bd9Sstevel@tonic-gate 		    corestat.st_rdev == kmemstat.st_rdev ||
1227c478bd9Sstevel@tonic-gate 		    corestat.st_rdev == allkmemstat.st_rdev) {
1237c478bd9Sstevel@tonic-gate 			char *kmem = (corestat.st_rdev == allkmemstat.st_rdev ?
1247c478bd9Sstevel@tonic-gate 			    "/dev/allkmem" : "/dev/kmem");
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 			if ((kd->kvm_kmemfd = open64(kmem, flag)) == -1)
1277c478bd9Sstevel@tonic-gate 				return (fail(kd, err, "cannot open %s", kmem));
1287c478bd9Sstevel@tonic-gate 			if ((kd->kvm_memfd = open64("/dev/mem", flag)) == -1)
1297c478bd9Sstevel@tonic-gate 				return (fail(kd, err, "cannot open /dev/mem"));
1307c478bd9Sstevel@tonic-gate 		}
1317c478bd9Sstevel@tonic-gate 	} else {
1327c478bd9Sstevel@tonic-gate 		if ((kd->kvm_corefd = open64(corefile, flag)) == -1)
1337c478bd9Sstevel@tonic-gate 			return (fail(kd, err, "cannot open %s", corefile));
1347c478bd9Sstevel@tonic-gate 		if (pread64(kd->kvm_corefd, &kd->kvm_dump,
1357c478bd9Sstevel@tonic-gate 		    sizeof (kd->kvm_dump), 0) != sizeof (kd->kvm_dump))
1367c478bd9Sstevel@tonic-gate 			return (fail(kd, err, "cannot read dump header"));
1377c478bd9Sstevel@tonic-gate 		if (kd->kvm_dump.dump_magic != DUMP_MAGIC)
1387c478bd9Sstevel@tonic-gate 			return (fail(kd, err, "%s is not a kernel core file "
1397c478bd9Sstevel@tonic-gate 			    "(bad magic number %x)", corefile,
1407c478bd9Sstevel@tonic-gate 			    kd->kvm_dump.dump_magic));
1417c478bd9Sstevel@tonic-gate 		if (kd->kvm_dump.dump_version != DUMP_VERSION)
1427c478bd9Sstevel@tonic-gate 			return (fail(kd, err,
1437c478bd9Sstevel@tonic-gate 			    "libkvm version (%u) != corefile version (%u)",
1447c478bd9Sstevel@tonic-gate 			    DUMP_VERSION, kd->kvm_dump.dump_version));
1457c478bd9Sstevel@tonic-gate 		if (kd->kvm_dump.dump_wordsize != DUMP_WORDSIZE)
1467c478bd9Sstevel@tonic-gate 			return (fail(kd, err, "%s is a %d-bit core file - "
1477c478bd9Sstevel@tonic-gate 			    "cannot examine with %d-bit libkvm", corefile,
1487c478bd9Sstevel@tonic-gate 			    kd->kvm_dump.dump_wordsize, DUMP_WORDSIZE));
1497c478bd9Sstevel@tonic-gate 		/*
1507c478bd9Sstevel@tonic-gate 		 * We try to mmap(2) the entire corefile for performance
1517c478bd9Sstevel@tonic-gate 		 * (so we can use bcopy(3C) rather than pread(2)).  Failing
1527c478bd9Sstevel@tonic-gate 		 * that, we insist on at least mmap(2)ing the dump map.
1537c478bd9Sstevel@tonic-gate 		 */
1547c478bd9Sstevel@tonic-gate 		kd->kvm_coremapsize = (size_t)corestat.st_size;
1557c478bd9Sstevel@tonic-gate 		if (corestat.st_size > LONG_MAX ||
1567c478bd9Sstevel@tonic-gate 		    (kd->kvm_core = mmap64(0, kd->kvm_coremapsize,
1577c478bd9Sstevel@tonic-gate 		    PROT_READ, MAP_SHARED, kd->kvm_corefd, 0)) == MAP_FAILED) {
1587c478bd9Sstevel@tonic-gate 			kd->kvm_coremapsize = kd->kvm_dump.dump_data;
1597c478bd9Sstevel@tonic-gate 			if ((kd->kvm_core = mmap64(0, kd->kvm_coremapsize,
1607c478bd9Sstevel@tonic-gate 			    PROT_READ, MAP_SHARED, kd->kvm_corefd, 0)) ==
1617c478bd9Sstevel@tonic-gate 			    MAP_FAILED)
1627c478bd9Sstevel@tonic-gate 				return (fail(kd, err, "cannot mmap corefile"));
1637c478bd9Sstevel@tonic-gate 		}
1647c478bd9Sstevel@tonic-gate 		kd->kvm_map = (void *)(kd->kvm_core + kd->kvm_dump.dump_map);
1657c478bd9Sstevel@tonic-gate 		kd->kvm_pfn = (void *)(kd->kvm_core + kd->kvm_dump.dump_pfn);
1667c478bd9Sstevel@tonic-gate 	}
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	if (namelist == NULL)
1697c478bd9Sstevel@tonic-gate 		namelist = "/dev/ksyms";
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate 	(void) strncpy(kd->kvm_namelist, namelist, MAXNAMELEN);
1727c478bd9Sstevel@tonic-gate 
173*1320caf7SBryan Cantrill 	if (kvm_nlist(kd, nl) == -1) {
174*1320caf7SBryan Cantrill 		if (kd->kvm_corefd == -1) {
175*1320caf7SBryan Cantrill 			return (fail(kd, err, "%s is not a %d-bit "
176*1320caf7SBryan Cantrill 			    "kernel namelist", namelist, DUMP_WORDSIZE));
177*1320caf7SBryan Cantrill 		}
178*1320caf7SBryan Cantrill 
179*1320caf7SBryan Cantrill 		if (kvm_nlist_core(kd, nl, err) == -1)
180*1320caf7SBryan Cantrill 			return (NULL);		/* fail() already called */
181*1320caf7SBryan Cantrill 	}
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 	kd->kvm_kas = (struct as *)nl[0].n_value;
1847c478bd9Sstevel@tonic-gate 	kd->kvm_practive = (proc_t *)nl[1].n_value;
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 	(void) kvm_setproc(kd);
1877c478bd9Sstevel@tonic-gate 	return (kd);
1887c478bd9Sstevel@tonic-gate }
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate int
1917c478bd9Sstevel@tonic-gate kvm_close(kvm_t *kd)
1927c478bd9Sstevel@tonic-gate {
1937c478bd9Sstevel@tonic-gate 	if (kd->kvm_core != NULL && kd->kvm_core != MAP_FAILED)
1947c478bd9Sstevel@tonic-gate 		(void) munmap(kd->kvm_core, kd->kvm_coremapsize);
1957c478bd9Sstevel@tonic-gate 	if (kd->kvm_corefd != -1)
1967c478bd9Sstevel@tonic-gate 		(void) close(kd->kvm_corefd);
1977c478bd9Sstevel@tonic-gate 	if (kd->kvm_kmemfd != -1)
1987c478bd9Sstevel@tonic-gate 		(void) close(kd->kvm_kmemfd);
1997c478bd9Sstevel@tonic-gate 	if (kd->kvm_memfd != -1)
2007c478bd9Sstevel@tonic-gate 		(void) close(kd->kvm_memfd);
201*1320caf7SBryan Cantrill 	if (kd->kvm_namelist_core)
202*1320caf7SBryan Cantrill 		(void) unlink(kd->kvm_namelist);
2037c478bd9Sstevel@tonic-gate 	free(kd);
2047c478bd9Sstevel@tonic-gate 	return (0);
2057c478bd9Sstevel@tonic-gate }
2067c478bd9Sstevel@tonic-gate 
207*1320caf7SBryan Cantrill const char *
208*1320caf7SBryan Cantrill kvm_namelist(kvm_t *kd)
209*1320caf7SBryan Cantrill {
210*1320caf7SBryan Cantrill 	return (kd->kvm_namelist);
211*1320caf7SBryan Cantrill }
212*1320caf7SBryan Cantrill 
2137c478bd9Sstevel@tonic-gate int
2147c478bd9Sstevel@tonic-gate kvm_nlist(kvm_t *kd, struct nlist nl[])
2157c478bd9Sstevel@tonic-gate {
2167c478bd9Sstevel@tonic-gate 	return (nlist(kd->kvm_namelist, nl));
2177c478bd9Sstevel@tonic-gate }
2187c478bd9Sstevel@tonic-gate 
219*1320caf7SBryan Cantrill /*
220*1320caf7SBryan Cantrill  * If we don't have a name list, try to dig it out of the kernel crash dump.
221*1320caf7SBryan Cantrill  * (The symbols have been present in the dump, uncompressed, for nearly a
222*1320caf7SBryan Cantrill  * decade as of this writing -- and it is frankly surprising that the archaic
223*1320caf7SBryan Cantrill  * notion of a disjoint symbol table managed to survive that change.)
224*1320caf7SBryan Cantrill  */
225*1320caf7SBryan Cantrill static int
226*1320caf7SBryan Cantrill kvm_nlist_core(kvm_t *kd, struct nlist nl[], const char *err)
227*1320caf7SBryan Cantrill {
228*1320caf7SBryan Cantrill 	dumphdr_t *dump = &kd->kvm_dump;
229*1320caf7SBryan Cantrill 	char *msg = "couldn't extract symbols from dump";
230*1320caf7SBryan Cantrill 	char *template = "/tmp/.libkvm.kvm_nlist_core.pid%d.XXXXXX";
231*1320caf7SBryan Cantrill 	int fd, rval;
232*1320caf7SBryan Cantrill 
233*1320caf7SBryan Cantrill 	if (dump->dump_ksyms_size != dump->dump_ksyms_csize) {
234*1320caf7SBryan Cantrill 		(void) fail(kd, err, "%s: kernel symbols are compressed", msg);
235*1320caf7SBryan Cantrill 		return (-1);
236*1320caf7SBryan Cantrill 	}
237*1320caf7SBryan Cantrill 
238*1320caf7SBryan Cantrill 	if (dump->dump_ksyms + dump->dump_ksyms_size > kd->kvm_coremapsize) {
239*1320caf7SBryan Cantrill 		(void) fail(kd, err, "%s: kernel symbols not mapped", msg);
240*1320caf7SBryan Cantrill 		return (-1);
241*1320caf7SBryan Cantrill 	}
242*1320caf7SBryan Cantrill 
243*1320caf7SBryan Cantrill 	/*
244*1320caf7SBryan Cantrill 	 * Beause this temporary file may be left as a turd if the caller
245*1320caf7SBryan Cantrill 	 * does not properly call kvm_close(), we make sure that it clearly
246*1320caf7SBryan Cantrill 	 * indicates its origins.
247*1320caf7SBryan Cantrill 	 */
248*1320caf7SBryan Cantrill 	(void) snprintf(kd->kvm_namelist, MAXNAMELEN, template, getpid());
249*1320caf7SBryan Cantrill 
250*1320caf7SBryan Cantrill 	if ((fd = mkstemp(kd->kvm_namelist)) == -1) {
251*1320caf7SBryan Cantrill 		(void) fail(kd, err, "%s: couldn't create temporary "
252*1320caf7SBryan Cantrill 		    "symbols file: %s", msg, strerror(errno));
253*1320caf7SBryan Cantrill 		return (-1);
254*1320caf7SBryan Cantrill 	}
255*1320caf7SBryan Cantrill 
256*1320caf7SBryan Cantrill 	kd->kvm_namelist_core = B_TRUE;
257*1320caf7SBryan Cantrill 
258*1320caf7SBryan Cantrill 	do {
259*1320caf7SBryan Cantrill 		rval = write(fd, (caddr_t)((uintptr_t)kd->kvm_core +
260*1320caf7SBryan Cantrill 		    (uintptr_t)dump->dump_ksyms), dump->dump_ksyms_size);
261*1320caf7SBryan Cantrill 	} while (rval < dump->dump_ksyms_size && errno == EINTR);
262*1320caf7SBryan Cantrill 
263*1320caf7SBryan Cantrill 	if (rval < dump->dump_ksyms_size) {
264*1320caf7SBryan Cantrill 		(void) fail(kd, err, "%s: couldn't write to temporary "
265*1320caf7SBryan Cantrill 		    "symbols file: %s", msg, strerror(errno));
266*1320caf7SBryan Cantrill 		(void) close(fd);
267*1320caf7SBryan Cantrill 		return (-1);
268*1320caf7SBryan Cantrill 	}
269*1320caf7SBryan Cantrill 
270*1320caf7SBryan Cantrill 	(void) close(fd);
271*1320caf7SBryan Cantrill 
272*1320caf7SBryan Cantrill 	if (kvm_nlist(kd, nl) == -1) {
273*1320caf7SBryan Cantrill 		(void) fail(kd, err, "%s: symbols not valid", msg);
274*1320caf7SBryan Cantrill 		return (-1);
275*1320caf7SBryan Cantrill 	}
276*1320caf7SBryan Cantrill 
277*1320caf7SBryan Cantrill 	return (0);
278*1320caf7SBryan Cantrill }
279*1320caf7SBryan Cantrill 
2807c478bd9Sstevel@tonic-gate static offset_t
2817c478bd9Sstevel@tonic-gate kvm_lookup(kvm_t *kd, struct as *as, uint64_t addr)
2827c478bd9Sstevel@tonic-gate {
2837c478bd9Sstevel@tonic-gate 	uintptr_t pageoff = addr & (kd->kvm_dump.dump_pagesize - 1);
2847c478bd9Sstevel@tonic-gate 	uint64_t page = addr - pageoff;
2857c478bd9Sstevel@tonic-gate 	offset_t off = 0;
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	if (kd->kvm_debug)
2887c478bd9Sstevel@tonic-gate 		fprintf(stderr, "kvm_lookup(%p, %llx):", (void *)as, addr);
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	if (as == NULL) {		/* physical addressing mode */
2917c478bd9Sstevel@tonic-gate 		long first = 0;
2927c478bd9Sstevel@tonic-gate 		long last = kd->kvm_dump.dump_npages - 1;
2937c478bd9Sstevel@tonic-gate 		pfn_t target = (pfn_t)(page >> kd->kvm_dump.dump_pageshift);
2947c478bd9Sstevel@tonic-gate 		while (last >= first) {
2957c478bd9Sstevel@tonic-gate 			long middle = (first + last) / 2;
2967c478bd9Sstevel@tonic-gate 			pfn_t pfn = kd->kvm_pfn[middle];
2977c478bd9Sstevel@tonic-gate 			if (kd->kvm_debug)
2987c478bd9Sstevel@tonic-gate 				fprintf(stderr, " %ld ->", middle);
2997c478bd9Sstevel@tonic-gate 			if (pfn == target) {
3007c478bd9Sstevel@tonic-gate 				off = kd->kvm_dump.dump_data + pageoff +
3017c478bd9Sstevel@tonic-gate 				    ((uint64_t)middle <<
3027c478bd9Sstevel@tonic-gate 				    kd->kvm_dump.dump_pageshift);
3037c478bd9Sstevel@tonic-gate 				break;
3047c478bd9Sstevel@tonic-gate 			}
3057c478bd9Sstevel@tonic-gate 			if (pfn < target)
3067c478bd9Sstevel@tonic-gate 				first = middle + 1;
3077c478bd9Sstevel@tonic-gate 			else
3087c478bd9Sstevel@tonic-gate 				last = middle - 1;
3097c478bd9Sstevel@tonic-gate 		}
3107c478bd9Sstevel@tonic-gate 	} else {
3117c478bd9Sstevel@tonic-gate 		long hash = DUMP_HASH(&kd->kvm_dump, as, page);
3127c478bd9Sstevel@tonic-gate 		off = kd->kvm_map[hash].dm_first;
3137c478bd9Sstevel@tonic-gate 		while (off != 0) {
3147c478bd9Sstevel@tonic-gate 			dump_map_t *dmp = (void *)(kd->kvm_core + off);
3157c478bd9Sstevel@tonic-gate 			if (kd->kvm_debug)
3167c478bd9Sstevel@tonic-gate 				fprintf(stderr, " %llx ->", off);
3177c478bd9Sstevel@tonic-gate 			if (dmp < kd->kvm_map ||
3187c478bd9Sstevel@tonic-gate 			    dmp > kd->kvm_map + kd->kvm_dump.dump_hashmask ||
3197c478bd9Sstevel@tonic-gate 			    (off & (sizeof (offset_t) - 1)) != 0 ||
3207c478bd9Sstevel@tonic-gate 			    DUMP_HASH(&kd->kvm_dump, dmp->dm_as, dmp->dm_va) !=
3217c478bd9Sstevel@tonic-gate 			    hash) {
3227c478bd9Sstevel@tonic-gate 				if (kd->kvm_debug)
3237c478bd9Sstevel@tonic-gate 					fprintf(stderr, " dump map corrupt\n");
3247c478bd9Sstevel@tonic-gate 				return (0);
3257c478bd9Sstevel@tonic-gate 			}
3267c478bd9Sstevel@tonic-gate 			if (dmp->dm_va == page && dmp->dm_as == as) {
3277c478bd9Sstevel@tonic-gate 				off = dmp->dm_data + pageoff;
3287c478bd9Sstevel@tonic-gate 				break;
3297c478bd9Sstevel@tonic-gate 			}
3307c478bd9Sstevel@tonic-gate 			off = dmp->dm_next;
3317c478bd9Sstevel@tonic-gate 		}
3327c478bd9Sstevel@tonic-gate 	}
3337c478bd9Sstevel@tonic-gate 	if (kd->kvm_debug)
3347c478bd9Sstevel@tonic-gate 		fprintf(stderr, "%s found: %llx\n", off ? "" : " not", off);
3357c478bd9Sstevel@tonic-gate 	return (off);
3367c478bd9Sstevel@tonic-gate }
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate static ssize_t
3397c478bd9Sstevel@tonic-gate kvm_rw(kvm_t *kd, uint64_t addr, void *buf, size_t size,
3407c478bd9Sstevel@tonic-gate 	struct as *as, ssize_t (*prw)(int, void *, size_t, offset_t))
3417c478bd9Sstevel@tonic-gate {
3427c478bd9Sstevel@tonic-gate 	offset_t off;
3437c478bd9Sstevel@tonic-gate 	size_t resid = size;
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	/*
3467c478bd9Sstevel@tonic-gate 	 * read/write of zero bytes always succeeds
3477c478bd9Sstevel@tonic-gate 	 */
3487c478bd9Sstevel@tonic-gate 	if (size == 0)
3497c478bd9Sstevel@tonic-gate 		return (0);
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	if (kd->kvm_core == NULL) {
3527c478bd9Sstevel@tonic-gate 		char procbuf[100];
3537c478bd9Sstevel@tonic-gate 		int procfd;
3547c478bd9Sstevel@tonic-gate 		ssize_t rval;
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 		if (as == kd->kvm_kas)
3577c478bd9Sstevel@tonic-gate 			return (prw(kd->kvm_kmemfd, buf, size, addr));
3587c478bd9Sstevel@tonic-gate 		if (as == NULL)
3597c478bd9Sstevel@tonic-gate 			return (prw(kd->kvm_memfd, buf, size, addr));
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 		(void) sprintf(procbuf, "/proc/%ld/as", kd->kvm_pid);
3627c478bd9Sstevel@tonic-gate 		if ((procfd = open64(procbuf, kd->kvm_openflag)) == -1)
3637c478bd9Sstevel@tonic-gate 			return (-1);
3647c478bd9Sstevel@tonic-gate 		rval = prw(procfd, buf, size, addr);
3657c478bd9Sstevel@tonic-gate 		(void) close(procfd);
3667c478bd9Sstevel@tonic-gate 		return (rval);
3677c478bd9Sstevel@tonic-gate 	}
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 	while (resid != 0) {
3707c478bd9Sstevel@tonic-gate 		uintptr_t pageoff = addr & (kd->kvm_dump.dump_pagesize - 1);
3717c478bd9Sstevel@tonic-gate 		ssize_t len = MIN(resid, kd->kvm_dump.dump_pagesize - pageoff);
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate 		if ((off = kvm_lookup(kd, as, addr)) == 0)
3747c478bd9Sstevel@tonic-gate 			break;
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 		if (prw == PREAD && off < kd->kvm_coremapsize)
3777c478bd9Sstevel@tonic-gate 			bcopy(kd->kvm_core + off, buf, len);
3787c478bd9Sstevel@tonic-gate 		else if ((len = prw(kd->kvm_corefd, buf, len, off)) <= 0)
3797c478bd9Sstevel@tonic-gate 			break;
3807c478bd9Sstevel@tonic-gate 		resid -= len;
3817c478bd9Sstevel@tonic-gate 		addr += len;
3827c478bd9Sstevel@tonic-gate 		buf = (char *)buf + len;
3837c478bd9Sstevel@tonic-gate 	}
3847c478bd9Sstevel@tonic-gate 	return (resid < size ? size - resid : -1);
3857c478bd9Sstevel@tonic-gate }
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate ssize_t
3887c478bd9Sstevel@tonic-gate kvm_read(kvm_t *kd, uintptr_t addr, void *buf, size_t size)
3897c478bd9Sstevel@tonic-gate {
3907c478bd9Sstevel@tonic-gate 	return (kvm_rw(kd, addr, buf, size, kd->kvm_kas, PREAD));
3917c478bd9Sstevel@tonic-gate }
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate ssize_t
3947c478bd9Sstevel@tonic-gate kvm_kread(kvm_t *kd, uintptr_t addr, void *buf, size_t size)
3957c478bd9Sstevel@tonic-gate {
3967c478bd9Sstevel@tonic-gate 	return (kvm_rw(kd, addr, buf, size, kd->kvm_kas, PREAD));
3977c478bd9Sstevel@tonic-gate }
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate ssize_t
4007c478bd9Sstevel@tonic-gate kvm_uread(kvm_t *kd, uintptr_t addr, void *buf, size_t size)
4017c478bd9Sstevel@tonic-gate {
4027c478bd9Sstevel@tonic-gate 	return (kvm_rw(kd, addr, buf, size, kd->kvm_proc.p_as, PREAD));
4037c478bd9Sstevel@tonic-gate }
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate ssize_t
4067c478bd9Sstevel@tonic-gate kvm_aread(kvm_t *kd, uintptr_t addr, void *buf, size_t size, struct as *as)
4077c478bd9Sstevel@tonic-gate {
4087c478bd9Sstevel@tonic-gate 	return (kvm_rw(kd, addr, buf, size, as, PREAD));
4097c478bd9Sstevel@tonic-gate }
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate ssize_t
4127c478bd9Sstevel@tonic-gate kvm_pread(kvm_t *kd, uint64_t addr, void *buf, size_t size)
4137c478bd9Sstevel@tonic-gate {
4147c478bd9Sstevel@tonic-gate 	return (kvm_rw(kd, addr, buf, size, NULL, PREAD));
4157c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate ssize_t
4187c478bd9Sstevel@tonic-gate kvm_write(kvm_t *kd, uintptr_t addr, const void *buf, size_t size)
4197c478bd9Sstevel@tonic-gate {
4207c478bd9Sstevel@tonic-gate 	return (kvm_rw(kd, addr, (void *)buf, size, kd->kvm_kas, PWRITE));
4217c478bd9Sstevel@tonic-gate }
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate ssize_t
4247c478bd9Sstevel@tonic-gate kvm_kwrite(kvm_t *kd, uintptr_t addr, const void *buf, size_t size)
4257c478bd9Sstevel@tonic-gate {
4267c478bd9Sstevel@tonic-gate 	return (kvm_rw(kd, addr, (void *)buf, size, kd->kvm_kas, PWRITE));
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate ssize_t
4307c478bd9Sstevel@tonic-gate kvm_uwrite(kvm_t *kd, uintptr_t addr, const void *buf, size_t size)
4317c478bd9Sstevel@tonic-gate {
4327c478bd9Sstevel@tonic-gate 	return (kvm_rw(kd, addr, (void *)buf, size, kd->kvm_proc.p_as, PWRITE));
4337c478bd9Sstevel@tonic-gate }
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate ssize_t
4367c478bd9Sstevel@tonic-gate kvm_awrite(kvm_t *kd, uintptr_t addr, const void *buf, size_t size,
4377c478bd9Sstevel@tonic-gate     struct as *as)
4387c478bd9Sstevel@tonic-gate {
4397c478bd9Sstevel@tonic-gate 	return (kvm_rw(kd, addr, (void *)buf, size, as, PWRITE));
4407c478bd9Sstevel@tonic-gate }
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate ssize_t
4437c478bd9Sstevel@tonic-gate kvm_pwrite(kvm_t *kd, uint64_t addr, const void *buf, size_t size)
4447c478bd9Sstevel@tonic-gate {
4457c478bd9Sstevel@tonic-gate 	return (kvm_rw(kd, addr, (void *)buf, size, NULL, PWRITE));
4467c478bd9Sstevel@tonic-gate }
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate uint64_t
4497c478bd9Sstevel@tonic-gate kvm_physaddr(kvm_t *kd, struct as *as, uintptr_t addr)
4507c478bd9Sstevel@tonic-gate {
4517c478bd9Sstevel@tonic-gate 	mem_vtop_t mem_vtop;
4527c478bd9Sstevel@tonic-gate 	offset_t off;
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 	if (kd->kvm_core == NULL) {
4557c478bd9Sstevel@tonic-gate 		mem_vtop.m_as = as;
4567c478bd9Sstevel@tonic-gate 		mem_vtop.m_va = (void *)addr;
4577c478bd9Sstevel@tonic-gate 		if (ioctl(kd->kvm_kmemfd, MEM_VTOP, &mem_vtop) == 0)
4587c478bd9Sstevel@tonic-gate 			return ((uint64_t)mem_vtop.m_pfn * getpagesize() +
4597c478bd9Sstevel@tonic-gate 			    (addr & (getpagesize() - 1)));
4607c478bd9Sstevel@tonic-gate 	} else {
4617c478bd9Sstevel@tonic-gate 		if ((off = kvm_lookup(kd, as, addr)) != 0) {
4627c478bd9Sstevel@tonic-gate 			long pfn_index =
4637c478bd9Sstevel@tonic-gate 			    (u_offset_t)(off - kd->kvm_dump.dump_data) >>
4647c478bd9Sstevel@tonic-gate 			    kd->kvm_dump.dump_pageshift;
4657c478bd9Sstevel@tonic-gate 			return (((uint64_t)kd->kvm_pfn[pfn_index] <<
4667c478bd9Sstevel@tonic-gate 			    kd->kvm_dump.dump_pageshift) +
4677c478bd9Sstevel@tonic-gate 			    (addr & (kd->kvm_dump.dump_pagesize - 1)));
4687c478bd9Sstevel@tonic-gate 		}
4697c478bd9Sstevel@tonic-gate 	}
4707c478bd9Sstevel@tonic-gate 	return (-1ULL);
4717c478bd9Sstevel@tonic-gate }
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate struct proc *
4747c478bd9Sstevel@tonic-gate kvm_getproc(kvm_t *kd, pid_t pid)
4757c478bd9Sstevel@tonic-gate {
4767c478bd9Sstevel@tonic-gate 	(void) kvm_setproc(kd);
4777c478bd9Sstevel@tonic-gate 	while (kvm_nextproc(kd) != NULL)
4787c478bd9Sstevel@tonic-gate 		if (kd->kvm_pid == pid)
4797c478bd9Sstevel@tonic-gate 			return (&kd->kvm_proc);
4807c478bd9Sstevel@tonic-gate 	return (NULL);
4817c478bd9Sstevel@tonic-gate }
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate struct proc *
4847c478bd9Sstevel@tonic-gate kvm_nextproc(kvm_t *kd)
4857c478bd9Sstevel@tonic-gate {
4867c478bd9Sstevel@tonic-gate 	if (kd->kvm_proc.p_next == NULL ||
4877c478bd9Sstevel@tonic-gate 	    kvm_kread(kd, (uintptr_t)kd->kvm_proc.p_next,
4887c478bd9Sstevel@tonic-gate 	    &kd->kvm_proc, sizeof (proc_t)) != sizeof (proc_t) ||
4897c478bd9Sstevel@tonic-gate 	    kvm_kread(kd, (uintptr_t)&kd->kvm_proc.p_pidp->pid_id,
4907c478bd9Sstevel@tonic-gate 	    &kd->kvm_pid, sizeof (pid_t)) != sizeof (pid_t))
4917c478bd9Sstevel@tonic-gate 		return (NULL);
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	return (&kd->kvm_proc);
4947c478bd9Sstevel@tonic-gate }
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate int
4977c478bd9Sstevel@tonic-gate kvm_setproc(kvm_t *kd)
4987c478bd9Sstevel@tonic-gate {
4997c478bd9Sstevel@tonic-gate 	(void) kvm_kread(kd, (uintptr_t)kd->kvm_practive,
5007c478bd9Sstevel@tonic-gate 	    &kd->kvm_proc.p_next, sizeof (proc_t *));
5017c478bd9Sstevel@tonic-gate 	kd->kvm_pid = -1;
5027c478bd9Sstevel@tonic-gate 	return (0);
5037c478bd9Sstevel@tonic-gate }
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5067c478bd9Sstevel@tonic-gate struct user *
5077c478bd9Sstevel@tonic-gate kvm_getu(kvm_t *kd, struct proc *p)
5087c478bd9Sstevel@tonic-gate {
5097c478bd9Sstevel@tonic-gate 	return (&p->p_user);
5107c478bd9Sstevel@tonic-gate }
511