xref: /illumos-gate/usr/src/uts/common/io/ksyms.c (revision 7e3dbf5b)
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
560946fe0Smec  * Common Development and Distribution License (the "License").
660946fe0Smec  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2260946fe0Smec  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * ksyms driver - exports a single symbol/string table for the kernel
297c478bd9Sstevel@tonic-gate  * by concatenating all the module symbol/string tables.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <sys/types.h>
337c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
347c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
357c478bd9Sstevel@tonic-gate #include <sys/uio.h>
367c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
377c478bd9Sstevel@tonic-gate #include <sys/cred.h>
387c478bd9Sstevel@tonic-gate #include <sys/mman.h>
397c478bd9Sstevel@tonic-gate #include <sys/errno.h>
407c478bd9Sstevel@tonic-gate #include <sys/stat.h>
417c478bd9Sstevel@tonic-gate #include <sys/conf.h>
427c478bd9Sstevel@tonic-gate #include <sys/debug.h>
437c478bd9Sstevel@tonic-gate #include <sys/kobj.h>
447c478bd9Sstevel@tonic-gate #include <sys/ksyms.h>
457c478bd9Sstevel@tonic-gate #include <sys/vmsystm.h>
467c478bd9Sstevel@tonic-gate #include <vm/seg_vn.h>
477c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
487c478bd9Sstevel@tonic-gate #include <sys/compress.h>
497c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
507c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
517c478bd9Sstevel@tonic-gate #include <sys/list.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate typedef struct ksyms_image {
547c478bd9Sstevel@tonic-gate 	caddr_t	ksyms_base;	/* base address of image */
557c478bd9Sstevel@tonic-gate 	size_t	ksyms_size;	/* size of image */
567c478bd9Sstevel@tonic-gate } ksyms_image_t;
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate typedef struct ksyms_buflist {
597c478bd9Sstevel@tonic-gate 	list_node_t	buflist_node;
607c478bd9Sstevel@tonic-gate 	char buf[1];
617c478bd9Sstevel@tonic-gate } ksyms_buflist_t;
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate typedef struct ksyms_buflist_hdr {
64*7e3dbf5bSToomas Soome 	list_t	blist;
657c478bd9Sstevel@tonic-gate 	int	nchunks;
667c478bd9Sstevel@tonic-gate 	ksyms_buflist_t *cur;
677c478bd9Sstevel@tonic-gate 	size_t	curbuf_off;
687c478bd9Sstevel@tonic-gate } ksyms_buflist_hdr_t;
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate #define	BUF_SIZE	(PAGESIZE - (size_t)offsetof(ksyms_buflist_t, buf))
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate int nksyms_clones;		/* tunable: max clones of this device */
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate static ksyms_image_t *ksyms_clones;	/* clone device array */
757c478bd9Sstevel@tonic-gate static dev_info_t *ksyms_devi;
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate static void
ksyms_bcopy(const void * srcptr,void * ptr,size_t rsize)787c478bd9Sstevel@tonic-gate ksyms_bcopy(const void *srcptr, void *ptr, size_t rsize)
797c478bd9Sstevel@tonic-gate {
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate 	size_t sz;
827c478bd9Sstevel@tonic-gate 	const char *src = (const char *)srcptr;
837c478bd9Sstevel@tonic-gate 	ksyms_buflist_hdr_t *hptr = (ksyms_buflist_hdr_t *)ptr;
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 	if (hptr->cur == NULL)
867c478bd9Sstevel@tonic-gate 		return;
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate 	while (rsize) {
897c478bd9Sstevel@tonic-gate 		sz = MIN(rsize, (BUF_SIZE - hptr->curbuf_off));
907c478bd9Sstevel@tonic-gate 		bcopy(src, (hptr->cur->buf + hptr->curbuf_off), sz);
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 		hptr->curbuf_off += sz;
937c478bd9Sstevel@tonic-gate 		if (hptr->curbuf_off == BUF_SIZE) {
947c478bd9Sstevel@tonic-gate 			hptr->curbuf_off = 0;
957c478bd9Sstevel@tonic-gate 			hptr->cur = list_next(&hptr->blist, hptr->cur);
967c478bd9Sstevel@tonic-gate 			if (hptr->cur == NULL)
977c478bd9Sstevel@tonic-gate 				break;
987c478bd9Sstevel@tonic-gate 		}
997c478bd9Sstevel@tonic-gate 		src += sz;
1007c478bd9Sstevel@tonic-gate 		rsize -= sz;
1017c478bd9Sstevel@tonic-gate 	}
1027c478bd9Sstevel@tonic-gate }
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate static void
ksyms_buflist_free(ksyms_buflist_hdr_t * hdr)1057c478bd9Sstevel@tonic-gate ksyms_buflist_free(ksyms_buflist_hdr_t *hdr)
1067c478bd9Sstevel@tonic-gate {
1077c478bd9Sstevel@tonic-gate 	ksyms_buflist_t *list;
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate 	while (list = list_head(&hdr->blist)) {
1107c478bd9Sstevel@tonic-gate 		list_remove(&hdr->blist, list);
1117c478bd9Sstevel@tonic-gate 		kmem_free(list, PAGESIZE);
1127c478bd9Sstevel@tonic-gate 	}
1137c478bd9Sstevel@tonic-gate 	list_destroy(&hdr->blist);
1147c478bd9Sstevel@tonic-gate 	hdr->cur = NULL;
1157c478bd9Sstevel@tonic-gate }
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate /*
1197c478bd9Sstevel@tonic-gate  * Allocate 'size'(rounded to BUF_SIZE) bytes in chunks of BUF_SIZE, and
1207c478bd9Sstevel@tonic-gate  * add it to the buf list.
1217c478bd9Sstevel@tonic-gate  * Returns the total size rounded to BUF_SIZE.
1227c478bd9Sstevel@tonic-gate  */
1237c478bd9Sstevel@tonic-gate static size_t
ksyms_buflist_alloc(ksyms_buflist_hdr_t * hdr,size_t size)1247c478bd9Sstevel@tonic-gate ksyms_buflist_alloc(ksyms_buflist_hdr_t *hdr, size_t size)
1257c478bd9Sstevel@tonic-gate {
1267c478bd9Sstevel@tonic-gate 	int chunks, i;
1277c478bd9Sstevel@tonic-gate 	ksyms_buflist_t *list;
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	chunks = howmany(size, BUF_SIZE);
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	if (hdr->nchunks >= chunks)
1327c478bd9Sstevel@tonic-gate 		return (hdr->nchunks * BUF_SIZE);
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	/*
1357c478bd9Sstevel@tonic-gate 	 * Allocate chunks - hdr->nchunks buffers and add them to
1367c478bd9Sstevel@tonic-gate 	 * the list.
1377c478bd9Sstevel@tonic-gate 	 */
1387c478bd9Sstevel@tonic-gate 	for (i = chunks - hdr->nchunks; i > 0; i--) {
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 		if ((list = kmem_alloc(PAGESIZE, KM_NOSLEEP)) == NULL)
1417c478bd9Sstevel@tonic-gate 			break;
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 		list_insert_tail(&hdr->blist, list);
1447c478bd9Sstevel@tonic-gate 	}
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 	/*
1477c478bd9Sstevel@tonic-gate 	 * If we are running short of memory, free memory allocated till now
1487c478bd9Sstevel@tonic-gate 	 * and return.
1497c478bd9Sstevel@tonic-gate 	 */
1507c478bd9Sstevel@tonic-gate 	if (i > 0) {
1517c478bd9Sstevel@tonic-gate 		ksyms_buflist_free(hdr);
1527c478bd9Sstevel@tonic-gate 		return (0);
1537c478bd9Sstevel@tonic-gate 	}
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 	hdr->nchunks = chunks;
1567c478bd9Sstevel@tonic-gate 	hdr->cur = list_head(&hdr->blist);
1577c478bd9Sstevel@tonic-gate 	hdr->curbuf_off = 0;
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	return (chunks * BUF_SIZE);
1607c478bd9Sstevel@tonic-gate }
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate /*
1637c478bd9Sstevel@tonic-gate  * rlen is in multiples of PAGESIZE
1647c478bd9Sstevel@tonic-gate  */
1657c478bd9Sstevel@tonic-gate static char *
ksyms_asmap(struct as * as,size_t rlen)1667c478bd9Sstevel@tonic-gate ksyms_asmap(struct as *as, size_t rlen)
1677c478bd9Sstevel@tonic-gate {
16860946fe0Smec 	char *addr = NULL;
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	as_rangelock(as);
1717c478bd9Sstevel@tonic-gate 	map_addr(&addr, rlen, 0, 1, 0);
1727c478bd9Sstevel@tonic-gate 	if (addr == NULL || as_map(as, addr, rlen, segvn_create, zfod_argsp)) {
1737c478bd9Sstevel@tonic-gate 		as_rangeunlock(as);
1747c478bd9Sstevel@tonic-gate 		return (NULL);
1757c478bd9Sstevel@tonic-gate 	}
1767c478bd9Sstevel@tonic-gate 	as_rangeunlock(as);
1777c478bd9Sstevel@tonic-gate 	return (addr);
1787c478bd9Sstevel@tonic-gate }
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate static char *
ksyms_mapin(ksyms_buflist_hdr_t * hdr,size_t size)1817c478bd9Sstevel@tonic-gate ksyms_mapin(ksyms_buflist_hdr_t *hdr, size_t size)
1827c478bd9Sstevel@tonic-gate {
1837c478bd9Sstevel@tonic-gate 	size_t sz, rlen = roundup(size, PAGESIZE);
1847c478bd9Sstevel@tonic-gate 	struct as *as = curproc->p_as;
1857c478bd9Sstevel@tonic-gate 	char *addr, *raddr;
1867c478bd9Sstevel@tonic-gate 	ksyms_buflist_t *list = list_head(&hdr->blist);
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	if ((addr = ksyms_asmap(as, rlen)) == NULL)
1897c478bd9Sstevel@tonic-gate 		return (NULL);
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 	raddr = addr;
1927c478bd9Sstevel@tonic-gate 	while (size > 0 && list != NULL) {
1937c478bd9Sstevel@tonic-gate 		sz = MIN(size, BUF_SIZE);
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 		if (copyout(list->buf, raddr, sz)) {
1967c478bd9Sstevel@tonic-gate 			(void) as_unmap(as, addr, rlen);
1977c478bd9Sstevel@tonic-gate 			return (NULL);
1987c478bd9Sstevel@tonic-gate 		}
1997c478bd9Sstevel@tonic-gate 		list = list_next(&hdr->blist, list);
2007c478bd9Sstevel@tonic-gate 		raddr += sz;
2017c478bd9Sstevel@tonic-gate 		size -= sz;
2027c478bd9Sstevel@tonic-gate 	}
2037c478bd9Sstevel@tonic-gate 	return (addr);
2047c478bd9Sstevel@tonic-gate }
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate /*
2077c478bd9Sstevel@tonic-gate  * Copy a snapshot of the kernel symbol table into the user's address space.
2087c478bd9Sstevel@tonic-gate  * The symbol table is copied in fragments so that we do not have to
2097c478bd9Sstevel@tonic-gate  * do a large kmem_alloc() which could fail/block if the kernel memory is
2107c478bd9Sstevel@tonic-gate  * fragmented.
2117c478bd9Sstevel@tonic-gate  */
2127c478bd9Sstevel@tonic-gate /* ARGSUSED */
2137c478bd9Sstevel@tonic-gate static int
ksyms_open(dev_t * devp,int flag,int otyp,struct cred * cred)2147c478bd9Sstevel@tonic-gate ksyms_open(dev_t *devp, int flag, int otyp, struct cred *cred)
2157c478bd9Sstevel@tonic-gate {
2167c478bd9Sstevel@tonic-gate 	minor_t clone;
2177c478bd9Sstevel@tonic-gate 	size_t size = 0;
2187c478bd9Sstevel@tonic-gate 	size_t realsize;
2197c478bd9Sstevel@tonic-gate 	char *addr;
2207c478bd9Sstevel@tonic-gate 	void *hptr = NULL;
2217c478bd9Sstevel@tonic-gate 	ksyms_buflist_hdr_t hdr;
2227c478bd9Sstevel@tonic-gate 	bzero(&hdr, sizeof (struct ksyms_buflist_hdr));
2237c478bd9Sstevel@tonic-gate 	list_create(&hdr.blist, PAGESIZE,
22460946fe0Smec 	    offsetof(ksyms_buflist_t, buflist_node));
2257c478bd9Sstevel@tonic-gate 
2264f7df455Scth 	if (getminor(*devp) != 0)
2274f7df455Scth 		return (ENXIO);
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	for (;;) {
2307c478bd9Sstevel@tonic-gate 		realsize = ksyms_snapshot(ksyms_bcopy, hptr, size);
2317c478bd9Sstevel@tonic-gate 		if (realsize <= size)
2327c478bd9Sstevel@tonic-gate 			break;
2337c478bd9Sstevel@tonic-gate 		size = realsize;
2347c478bd9Sstevel@tonic-gate 		size = ksyms_buflist_alloc(&hdr, size);
2357c478bd9Sstevel@tonic-gate 		if (size == 0)
2367c478bd9Sstevel@tonic-gate 			return (ENOMEM);
2377c478bd9Sstevel@tonic-gate 		hptr = (void *)&hdr;
2387c478bd9Sstevel@tonic-gate 	}
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	addr = ksyms_mapin(&hdr, realsize);
2417c478bd9Sstevel@tonic-gate 	ksyms_buflist_free(&hdr);
2427c478bd9Sstevel@tonic-gate 	if (addr == NULL)
2437c478bd9Sstevel@tonic-gate 		return (EOVERFLOW);
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	/*
2467c478bd9Sstevel@tonic-gate 	 * Reserve a clone entry.  Note that we don't use clone 0
2477c478bd9Sstevel@tonic-gate 	 * since that's the "real" minor number.
2487c478bd9Sstevel@tonic-gate 	 */
2497c478bd9Sstevel@tonic-gate 	for (clone = 1; clone < nksyms_clones; clone++) {
25075d94465SJosef 'Jeff' Sipek 		if (atomic_cas_ptr(&ksyms_clones[clone].ksyms_base, 0, addr) ==
25175d94465SJosef 'Jeff' Sipek 		    0) {
2527c478bd9Sstevel@tonic-gate 			ksyms_clones[clone].ksyms_size = realsize;
2537c478bd9Sstevel@tonic-gate 			*devp = makedevice(getemajor(*devp), clone);
2547c478bd9Sstevel@tonic-gate 			(void) ddi_prop_update_int(*devp, ksyms_devi,
2557c478bd9Sstevel@tonic-gate 			    "size", realsize);
2567c478bd9Sstevel@tonic-gate 			modunload_disable();
2577c478bd9Sstevel@tonic-gate 			return (0);
2587c478bd9Sstevel@tonic-gate 		}
2597c478bd9Sstevel@tonic-gate 	}
2607c478bd9Sstevel@tonic-gate 	cmn_err(CE_NOTE, "ksyms: too many open references");
2617c478bd9Sstevel@tonic-gate 	(void) as_unmap(curproc->p_as, addr, roundup(realsize, PAGESIZE));
2627c478bd9Sstevel@tonic-gate 	return (ENXIO);
2637c478bd9Sstevel@tonic-gate }
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate /* ARGSUSED */
2667c478bd9Sstevel@tonic-gate static int
ksyms_close(dev_t dev,int flag,int otyp,struct cred * cred)2677c478bd9Sstevel@tonic-gate ksyms_close(dev_t dev, int flag, int otyp, struct cred *cred)
2687c478bd9Sstevel@tonic-gate {
2697c478bd9Sstevel@tonic-gate 	minor_t clone = getminor(dev);
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	(void) as_unmap(curproc->p_as, ksyms_clones[clone].ksyms_base,
2727c478bd9Sstevel@tonic-gate 	    roundup(ksyms_clones[clone].ksyms_size, PAGESIZE));
2737c478bd9Sstevel@tonic-gate 	ksyms_clones[clone].ksyms_base = 0;
2747c478bd9Sstevel@tonic-gate 	modunload_enable();
2757c478bd9Sstevel@tonic-gate 	(void) ddi_prop_remove(dev, ksyms_devi, "size");
2767c478bd9Sstevel@tonic-gate 	return (0);
2777c478bd9Sstevel@tonic-gate }
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate static int
ksyms_symtbl_copy(ksyms_image_t * kip,struct uio * uio,size_t len)2807c478bd9Sstevel@tonic-gate ksyms_symtbl_copy(ksyms_image_t *kip, struct uio *uio, size_t len)
2817c478bd9Sstevel@tonic-gate {
2827c478bd9Sstevel@tonic-gate 	char *buf;
2837c478bd9Sstevel@tonic-gate 	int error = 0;
2847c478bd9Sstevel@tonic-gate 	caddr_t base;
2857c478bd9Sstevel@tonic-gate 	off_t off = uio->uio_offset;
2867c478bd9Sstevel@tonic-gate 	size_t size;
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	/*
2897c478bd9Sstevel@tonic-gate 	 * The symbol table is stored in the user address space,
2907c478bd9Sstevel@tonic-gate 	 * so we have to copy it into the kernel first,
2917c478bd9Sstevel@tonic-gate 	 * then copy it back out to the specified user address.
2927c478bd9Sstevel@tonic-gate 	 */
2937c478bd9Sstevel@tonic-gate 	buf = kmem_alloc(PAGESIZE, KM_SLEEP);
2947c478bd9Sstevel@tonic-gate 	base = kip->ksyms_base + off;
2957c478bd9Sstevel@tonic-gate 	while (len) {
2967c478bd9Sstevel@tonic-gate 		size = MIN(PAGESIZE, len);
2977c478bd9Sstevel@tonic-gate 		if (copyin(base, buf, size))
2987c478bd9Sstevel@tonic-gate 			error = EFAULT;
2997c478bd9Sstevel@tonic-gate 		else
3007c478bd9Sstevel@tonic-gate 			error = uiomove(buf, size, UIO_READ, uio);
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 		if (error)
3037c478bd9Sstevel@tonic-gate 			break;
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 		len -= size;
3067c478bd9Sstevel@tonic-gate 		base += size;
3077c478bd9Sstevel@tonic-gate 	}
3087c478bd9Sstevel@tonic-gate 	kmem_free(buf, PAGESIZE);
3097c478bd9Sstevel@tonic-gate 	return (error);
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate /* ARGSUSED */
3137c478bd9Sstevel@tonic-gate static int
ksyms_read(dev_t dev,struct uio * uio,struct cred * cred)3147c478bd9Sstevel@tonic-gate ksyms_read(dev_t dev, struct uio *uio, struct cred *cred)
3157c478bd9Sstevel@tonic-gate {
3167c478bd9Sstevel@tonic-gate 	ksyms_image_t *kip = &ksyms_clones[getminor(dev)];
3177c478bd9Sstevel@tonic-gate 	off_t off = uio->uio_offset;
3187c478bd9Sstevel@tonic-gate 	size_t len = uio->uio_resid;
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 	if (off < 0 || off > kip->ksyms_size)
3217c478bd9Sstevel@tonic-gate 		return (EFAULT);
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	if (len > kip->ksyms_size - off)
3247c478bd9Sstevel@tonic-gate 		len = kip->ksyms_size - off;
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	if (len == 0)
3277c478bd9Sstevel@tonic-gate 		return (0);
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 	return (ksyms_symtbl_copy(kip, uio, len));
3307c478bd9Sstevel@tonic-gate }
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate /* ARGSUSED */
3337c478bd9Sstevel@tonic-gate static int
ksyms_segmap(dev_t dev,off_t off,struct as * as,caddr_t * addrp,off_t len,uint_t prot,uint_t maxprot,uint_t flags,struct cred * cred)3347c478bd9Sstevel@tonic-gate ksyms_segmap(dev_t dev, off_t off, struct as *as, caddr_t *addrp, off_t len,
3357c478bd9Sstevel@tonic-gate     uint_t prot, uint_t maxprot, uint_t flags, struct cred *cred)
3367c478bd9Sstevel@tonic-gate {
3377c478bd9Sstevel@tonic-gate 	ksyms_image_t *kip = &ksyms_clones[getminor(dev)];
3387c478bd9Sstevel@tonic-gate 	int error = 0;
3397c478bd9Sstevel@tonic-gate 	char *addr = NULL;
3407c478bd9Sstevel@tonic-gate 	size_t rlen = 0;
3417c478bd9Sstevel@tonic-gate 	struct iovec aiov;
3427c478bd9Sstevel@tonic-gate 	struct uio auio;
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	if (flags & MAP_FIXED)
3457c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	if (off < 0 || len <= 0 || off > kip->ksyms_size ||
34860946fe0Smec 	    len > kip->ksyms_size - off)
3497c478bd9Sstevel@tonic-gate 		return (EINVAL);
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	rlen = roundup(len, PAGESIZE);
3527c478bd9Sstevel@tonic-gate 	if ((addr = ksyms_asmap(as, rlen)) == NULL)
3537c478bd9Sstevel@tonic-gate 		return (EOVERFLOW);
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 	aiov.iov_base = addr;
3567c478bd9Sstevel@tonic-gate 	aiov.iov_len = len;
3577c478bd9Sstevel@tonic-gate 	auio.uio_offset = off;
3587c478bd9Sstevel@tonic-gate 	auio.uio_iov = &aiov;
3597c478bd9Sstevel@tonic-gate 	auio.uio_iovcnt = 1;
3607c478bd9Sstevel@tonic-gate 	auio.uio_resid = len;
3617c478bd9Sstevel@tonic-gate 	auio.uio_segflg = UIO_USERSPACE;
3627c478bd9Sstevel@tonic-gate 	auio.uio_llimit = MAXOFFSET_T;
3637c478bd9Sstevel@tonic-gate 	auio.uio_fmode = FREAD;
3647c478bd9Sstevel@tonic-gate 	auio.uio_extflg = UIO_COPY_CACHED;
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	error = ksyms_symtbl_copy(kip, &auio, len);
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	if (error)
3697c478bd9Sstevel@tonic-gate 		(void) as_unmap(as, addr, rlen);
3707c478bd9Sstevel@tonic-gate 	else
3717c478bd9Sstevel@tonic-gate 		*addrp = addr;
3727c478bd9Sstevel@tonic-gate 	return (error);
3737c478bd9Sstevel@tonic-gate }
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate /* ARGSUSED */
3767c478bd9Sstevel@tonic-gate static int
ksyms_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)3777c478bd9Sstevel@tonic-gate ksyms_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
3787c478bd9Sstevel@tonic-gate {
3797c478bd9Sstevel@tonic-gate 	switch (infocmd) {
3807c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
3817c478bd9Sstevel@tonic-gate 		*result = ksyms_devi;
3827c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
3837c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
3847c478bd9Sstevel@tonic-gate 		*result = 0;
3857c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
3867c478bd9Sstevel@tonic-gate 	}
3877c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
3887c478bd9Sstevel@tonic-gate }
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate static int
ksyms_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)3917c478bd9Sstevel@tonic-gate ksyms_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
3927c478bd9Sstevel@tonic-gate {
3937c478bd9Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
3947c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
395*7e3dbf5bSToomas Soome 	if (ddi_create_minor_node(devi, "ksyms", S_IFCHR, 0, DDI_PSEUDO, 0)
3967c478bd9Sstevel@tonic-gate 	    == DDI_FAILURE) {
3977c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(devi, NULL);
3987c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3997c478bd9Sstevel@tonic-gate 	}
4007c478bd9Sstevel@tonic-gate 	ksyms_devi = devi;
4017c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4027c478bd9Sstevel@tonic-gate }
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate static int
ksyms_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)4057c478bd9Sstevel@tonic-gate ksyms_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
4067c478bd9Sstevel@tonic-gate {
4077c478bd9Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
4087c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4097c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(devi, NULL);
4107c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4117c478bd9Sstevel@tonic-gate }
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate static struct cb_ops ksyms_cb_ops = {
4147c478bd9Sstevel@tonic-gate 	ksyms_open,		/* open */
4157c478bd9Sstevel@tonic-gate 	ksyms_close,		/* close */
4167c478bd9Sstevel@tonic-gate 	nodev,			/* strategy */
4177c478bd9Sstevel@tonic-gate 	nodev,			/* print */
4187c478bd9Sstevel@tonic-gate 	nodev,			/* dump */
4197c478bd9Sstevel@tonic-gate 	ksyms_read,		/* read */
4207c478bd9Sstevel@tonic-gate 	nodev,			/* write */
4217c478bd9Sstevel@tonic-gate 	nodev,			/* ioctl */
4227c478bd9Sstevel@tonic-gate 	nodev,			/* devmap */
4237c478bd9Sstevel@tonic-gate 	nodev,			/* mmap */
4247c478bd9Sstevel@tonic-gate 	ksyms_segmap,		/* segmap */
4257c478bd9Sstevel@tonic-gate 	nochpoll,		/* poll */
4267c478bd9Sstevel@tonic-gate 	ddi_prop_op,		/* prop_op */
4277c478bd9Sstevel@tonic-gate 	0,			/* streamtab  */
4287c478bd9Sstevel@tonic-gate 	D_NEW | D_MP		/* Driver compatibility flag */
4297c478bd9Sstevel@tonic-gate };
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate static struct dev_ops ksyms_ops = {
4327c478bd9Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev, */
4337c478bd9Sstevel@tonic-gate 	0,			/* refcnt  */
4347c478bd9Sstevel@tonic-gate 	ksyms_info,		/* info */
4357c478bd9Sstevel@tonic-gate 	nulldev,		/* identify */
4367c478bd9Sstevel@tonic-gate 	nulldev,		/* probe */
4377c478bd9Sstevel@tonic-gate 	ksyms_attach,		/* attach */
4387c478bd9Sstevel@tonic-gate 	ksyms_detach,		/* detach */
4397c478bd9Sstevel@tonic-gate 	nodev,			/* reset */
4407c478bd9Sstevel@tonic-gate 	&ksyms_cb_ops,		/* driver operations */
44119397407SSherry Moore 	(struct bus_ops *)0,	/* no bus operations */
44219397407SSherry Moore 	NULL,			/* power */
44319397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
4447c478bd9Sstevel@tonic-gate };
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
44719397407SSherry Moore 	&mod_driverops, "kernel symbols driver", &ksyms_ops,
4487c478bd9Sstevel@tonic-gate };
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
4517c478bd9Sstevel@tonic-gate 	MODREV_1, { (void *)&modldrv }
4527c478bd9Sstevel@tonic-gate };
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate int
_init(void)4557c478bd9Sstevel@tonic-gate _init(void)
4567c478bd9Sstevel@tonic-gate {
4577c478bd9Sstevel@tonic-gate 	int error;
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	if (nksyms_clones == 0)
4607c478bd9Sstevel@tonic-gate 		nksyms_clones = maxusers + 50;
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate 	ksyms_clones = kmem_zalloc(nksyms_clones *
4637c478bd9Sstevel@tonic-gate 	    sizeof (ksyms_image_t), KM_SLEEP);
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	if ((error = mod_install(&modlinkage)) != 0)
4667c478bd9Sstevel@tonic-gate 		kmem_free(ksyms_clones, nksyms_clones * sizeof (ksyms_image_t));
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	return (error);
4697c478bd9Sstevel@tonic-gate }
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate int
_fini(void)4727c478bd9Sstevel@tonic-gate _fini(void)
4737c478bd9Sstevel@tonic-gate {
4747c478bd9Sstevel@tonic-gate 	int error;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	if ((error = mod_remove(&modlinkage)) == 0)
4777c478bd9Sstevel@tonic-gate 		kmem_free(ksyms_clones, nksyms_clones * sizeof (ksyms_image_t));
4787c478bd9Sstevel@tonic-gate 	return (error);
4797c478bd9Sstevel@tonic-gate }
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)4827c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
4837c478bd9Sstevel@tonic-gate {
4847c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
4857c478bd9Sstevel@tonic-gate }
486