xref: /titanic_50/usr/src/uts/common/io/ksyms.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * ksyms driver - exports a single symbol/string table for the kernel
31*7c478bd9Sstevel@tonic-gate  * by concatenating all the module symbol/string tables.
32*7c478bd9Sstevel@tonic-gate  */
33*7c478bd9Sstevel@tonic-gate 
34*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
37*7c478bd9Sstevel@tonic-gate #include <sys/uio.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/cred.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/mman.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/errno.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/conf.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/debug.h>
45*7c478bd9Sstevel@tonic-gate #include <sys/kobj.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/ksyms.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/vmsystm.h>
48*7c478bd9Sstevel@tonic-gate #include <vm/seg_vn.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/compress.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
52*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
53*7c478bd9Sstevel@tonic-gate #include <sys/list.h>
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate typedef struct ksyms_image {
56*7c478bd9Sstevel@tonic-gate 	caddr_t	ksyms_base;	/* base address of image */
57*7c478bd9Sstevel@tonic-gate 	size_t	ksyms_size;	/* size of image */
58*7c478bd9Sstevel@tonic-gate } ksyms_image_t;
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate typedef struct ksyms_buflist {
61*7c478bd9Sstevel@tonic-gate 	list_node_t	buflist_node;
62*7c478bd9Sstevel@tonic-gate 	char buf[1];
63*7c478bd9Sstevel@tonic-gate } ksyms_buflist_t;
64*7c478bd9Sstevel@tonic-gate 
65*7c478bd9Sstevel@tonic-gate typedef struct ksyms_buflist_hdr {
66*7c478bd9Sstevel@tonic-gate 	list_t 	blist;
67*7c478bd9Sstevel@tonic-gate 	int	nchunks;
68*7c478bd9Sstevel@tonic-gate 	ksyms_buflist_t *cur;
69*7c478bd9Sstevel@tonic-gate 	size_t	curbuf_off;
70*7c478bd9Sstevel@tonic-gate } ksyms_buflist_hdr_t;
71*7c478bd9Sstevel@tonic-gate 
72*7c478bd9Sstevel@tonic-gate #define	BUF_SIZE	(PAGESIZE - (size_t)offsetof(ksyms_buflist_t, buf))
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate int nksyms_clones;		/* tunable: max clones of this device */
75*7c478bd9Sstevel@tonic-gate 
76*7c478bd9Sstevel@tonic-gate static ksyms_image_t *ksyms_clones;	/* clone device array */
77*7c478bd9Sstevel@tonic-gate static dev_info_t *ksyms_devi;
78*7c478bd9Sstevel@tonic-gate 
79*7c478bd9Sstevel@tonic-gate static void
80*7c478bd9Sstevel@tonic-gate ksyms_bcopy(const void *srcptr, void *ptr, size_t rsize)
81*7c478bd9Sstevel@tonic-gate {
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate 	size_t sz;
84*7c478bd9Sstevel@tonic-gate 	const char *src = (const char *)srcptr;
85*7c478bd9Sstevel@tonic-gate 	ksyms_buflist_hdr_t *hptr = (ksyms_buflist_hdr_t *)ptr;
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate 	if (hptr->cur == NULL)
88*7c478bd9Sstevel@tonic-gate 		return;
89*7c478bd9Sstevel@tonic-gate 
90*7c478bd9Sstevel@tonic-gate 	while (rsize) {
91*7c478bd9Sstevel@tonic-gate 		sz = MIN(rsize, (BUF_SIZE - hptr->curbuf_off));
92*7c478bd9Sstevel@tonic-gate 		bcopy(src, (hptr->cur->buf + hptr->curbuf_off), sz);
93*7c478bd9Sstevel@tonic-gate 
94*7c478bd9Sstevel@tonic-gate 		hptr->curbuf_off += sz;
95*7c478bd9Sstevel@tonic-gate 		if (hptr->curbuf_off == BUF_SIZE) {
96*7c478bd9Sstevel@tonic-gate 			hptr->curbuf_off = 0;
97*7c478bd9Sstevel@tonic-gate 			hptr->cur = list_next(&hptr->blist, hptr->cur);
98*7c478bd9Sstevel@tonic-gate 			if (hptr->cur == NULL)
99*7c478bd9Sstevel@tonic-gate 				break;
100*7c478bd9Sstevel@tonic-gate 		}
101*7c478bd9Sstevel@tonic-gate 		src += sz;
102*7c478bd9Sstevel@tonic-gate 		rsize -= sz;
103*7c478bd9Sstevel@tonic-gate 	}
104*7c478bd9Sstevel@tonic-gate }
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate static void
107*7c478bd9Sstevel@tonic-gate ksyms_buflist_free(ksyms_buflist_hdr_t *hdr)
108*7c478bd9Sstevel@tonic-gate {
109*7c478bd9Sstevel@tonic-gate 	ksyms_buflist_t *list;
110*7c478bd9Sstevel@tonic-gate 
111*7c478bd9Sstevel@tonic-gate 	while (list = list_head(&hdr->blist)) {
112*7c478bd9Sstevel@tonic-gate 		list_remove(&hdr->blist, list);
113*7c478bd9Sstevel@tonic-gate 		kmem_free(list, PAGESIZE);
114*7c478bd9Sstevel@tonic-gate 	}
115*7c478bd9Sstevel@tonic-gate 	list_destroy(&hdr->blist);
116*7c478bd9Sstevel@tonic-gate 	hdr->cur = NULL;
117*7c478bd9Sstevel@tonic-gate }
118*7c478bd9Sstevel@tonic-gate 
119*7c478bd9Sstevel@tonic-gate 
120*7c478bd9Sstevel@tonic-gate /*
121*7c478bd9Sstevel@tonic-gate  * Allocate 'size'(rounded to BUF_SIZE) bytes in chunks of BUF_SIZE, and
122*7c478bd9Sstevel@tonic-gate  * add it to the buf list.
123*7c478bd9Sstevel@tonic-gate  * Returns the total size rounded to BUF_SIZE.
124*7c478bd9Sstevel@tonic-gate  */
125*7c478bd9Sstevel@tonic-gate static size_t
126*7c478bd9Sstevel@tonic-gate ksyms_buflist_alloc(ksyms_buflist_hdr_t *hdr, size_t size)
127*7c478bd9Sstevel@tonic-gate {
128*7c478bd9Sstevel@tonic-gate 	int chunks, i;
129*7c478bd9Sstevel@tonic-gate 	ksyms_buflist_t *list;
130*7c478bd9Sstevel@tonic-gate 
131*7c478bd9Sstevel@tonic-gate 	chunks = howmany(size, BUF_SIZE);
132*7c478bd9Sstevel@tonic-gate 
133*7c478bd9Sstevel@tonic-gate 	if (hdr->nchunks >= chunks)
134*7c478bd9Sstevel@tonic-gate 		return (hdr->nchunks * BUF_SIZE);
135*7c478bd9Sstevel@tonic-gate 
136*7c478bd9Sstevel@tonic-gate 	/*
137*7c478bd9Sstevel@tonic-gate 	 * Allocate chunks - hdr->nchunks buffers and add them to
138*7c478bd9Sstevel@tonic-gate 	 * the list.
139*7c478bd9Sstevel@tonic-gate 	 */
140*7c478bd9Sstevel@tonic-gate 	for (i = chunks - hdr->nchunks; i > 0; i--) {
141*7c478bd9Sstevel@tonic-gate 
142*7c478bd9Sstevel@tonic-gate 		if ((list = kmem_alloc(PAGESIZE, KM_NOSLEEP)) == NULL)
143*7c478bd9Sstevel@tonic-gate 			break;
144*7c478bd9Sstevel@tonic-gate 
145*7c478bd9Sstevel@tonic-gate 		list_insert_tail(&hdr->blist, list);
146*7c478bd9Sstevel@tonic-gate 	}
147*7c478bd9Sstevel@tonic-gate 
148*7c478bd9Sstevel@tonic-gate 	/*
149*7c478bd9Sstevel@tonic-gate 	 * If we are running short of memory, free memory allocated till now
150*7c478bd9Sstevel@tonic-gate 	 * and return.
151*7c478bd9Sstevel@tonic-gate 	 */
152*7c478bd9Sstevel@tonic-gate 	if (i > 0) {
153*7c478bd9Sstevel@tonic-gate 		ksyms_buflist_free(hdr);
154*7c478bd9Sstevel@tonic-gate 		return (0);
155*7c478bd9Sstevel@tonic-gate 	}
156*7c478bd9Sstevel@tonic-gate 
157*7c478bd9Sstevel@tonic-gate 	hdr->nchunks = chunks;
158*7c478bd9Sstevel@tonic-gate 	hdr->cur = list_head(&hdr->blist);
159*7c478bd9Sstevel@tonic-gate 	hdr->curbuf_off = 0;
160*7c478bd9Sstevel@tonic-gate 
161*7c478bd9Sstevel@tonic-gate 	return (chunks * BUF_SIZE);
162*7c478bd9Sstevel@tonic-gate }
163*7c478bd9Sstevel@tonic-gate 
164*7c478bd9Sstevel@tonic-gate /*
165*7c478bd9Sstevel@tonic-gate  * rlen is in multiples of PAGESIZE
166*7c478bd9Sstevel@tonic-gate  */
167*7c478bd9Sstevel@tonic-gate static char *
168*7c478bd9Sstevel@tonic-gate ksyms_asmap(struct as *as, size_t rlen)
169*7c478bd9Sstevel@tonic-gate {
170*7c478bd9Sstevel@tonic-gate 	char *addr;
171*7c478bd9Sstevel@tonic-gate 
172*7c478bd9Sstevel@tonic-gate 	as_rangelock(as);
173*7c478bd9Sstevel@tonic-gate 	map_addr(&addr, rlen, 0, 1, 0);
174*7c478bd9Sstevel@tonic-gate 	if (addr == NULL || as_map(as, addr, rlen, segvn_create, zfod_argsp)) {
175*7c478bd9Sstevel@tonic-gate 		as_rangeunlock(as);
176*7c478bd9Sstevel@tonic-gate 		return (NULL);
177*7c478bd9Sstevel@tonic-gate 	}
178*7c478bd9Sstevel@tonic-gate 	as_rangeunlock(as);
179*7c478bd9Sstevel@tonic-gate 	return (addr);
180*7c478bd9Sstevel@tonic-gate }
181*7c478bd9Sstevel@tonic-gate 
182*7c478bd9Sstevel@tonic-gate static char *
183*7c478bd9Sstevel@tonic-gate ksyms_mapin(ksyms_buflist_hdr_t *hdr, size_t size)
184*7c478bd9Sstevel@tonic-gate {
185*7c478bd9Sstevel@tonic-gate 	size_t sz, rlen = roundup(size, PAGESIZE);
186*7c478bd9Sstevel@tonic-gate 	struct as *as = curproc->p_as;
187*7c478bd9Sstevel@tonic-gate 	char *addr, *raddr;
188*7c478bd9Sstevel@tonic-gate 	ksyms_buflist_t *list = list_head(&hdr->blist);
189*7c478bd9Sstevel@tonic-gate 
190*7c478bd9Sstevel@tonic-gate 	if ((addr = ksyms_asmap(as, rlen)) == NULL)
191*7c478bd9Sstevel@tonic-gate 		return (NULL);
192*7c478bd9Sstevel@tonic-gate 
193*7c478bd9Sstevel@tonic-gate 	raddr = addr;
194*7c478bd9Sstevel@tonic-gate 	while (size > 0 && list != NULL) {
195*7c478bd9Sstevel@tonic-gate 		sz = MIN(size, BUF_SIZE);
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate 		if (copyout(list->buf, raddr, sz)) {
198*7c478bd9Sstevel@tonic-gate 			(void) as_unmap(as, addr, rlen);
199*7c478bd9Sstevel@tonic-gate 			return (NULL);
200*7c478bd9Sstevel@tonic-gate 		}
201*7c478bd9Sstevel@tonic-gate 		list = list_next(&hdr->blist, list);
202*7c478bd9Sstevel@tonic-gate 		raddr += sz;
203*7c478bd9Sstevel@tonic-gate 		size -= sz;
204*7c478bd9Sstevel@tonic-gate 	}
205*7c478bd9Sstevel@tonic-gate 	return (addr);
206*7c478bd9Sstevel@tonic-gate }
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate /*
209*7c478bd9Sstevel@tonic-gate  * Copy a snapshot of the kernel symbol table into the user's address space.
210*7c478bd9Sstevel@tonic-gate  * The symbol table is copied in fragments so that we do not have to
211*7c478bd9Sstevel@tonic-gate  * do a large kmem_alloc() which could fail/block if the kernel memory is
212*7c478bd9Sstevel@tonic-gate  * fragmented.
213*7c478bd9Sstevel@tonic-gate  */
214*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
215*7c478bd9Sstevel@tonic-gate static int
216*7c478bd9Sstevel@tonic-gate ksyms_open(dev_t *devp, int flag, int otyp, struct cred *cred)
217*7c478bd9Sstevel@tonic-gate {
218*7c478bd9Sstevel@tonic-gate 	minor_t clone;
219*7c478bd9Sstevel@tonic-gate 	size_t size = 0;
220*7c478bd9Sstevel@tonic-gate 	size_t realsize;
221*7c478bd9Sstevel@tonic-gate 	char *addr;
222*7c478bd9Sstevel@tonic-gate 	void *hptr = NULL;
223*7c478bd9Sstevel@tonic-gate 	ksyms_buflist_hdr_t hdr;
224*7c478bd9Sstevel@tonic-gate 	bzero(&hdr, sizeof (struct ksyms_buflist_hdr));
225*7c478bd9Sstevel@tonic-gate 	list_create(&hdr.blist, PAGESIZE,
226*7c478bd9Sstevel@tonic-gate 				offsetof(ksyms_buflist_t, buflist_node));
227*7c478bd9Sstevel@tonic-gate 
228*7c478bd9Sstevel@tonic-gate 	ASSERT(getminor(*devp) == 0);
229*7c478bd9Sstevel@tonic-gate 
230*7c478bd9Sstevel@tonic-gate 	for (;;) {
231*7c478bd9Sstevel@tonic-gate 		realsize = ksyms_snapshot(ksyms_bcopy, hptr, size);
232*7c478bd9Sstevel@tonic-gate 		if (realsize <= size)
233*7c478bd9Sstevel@tonic-gate 			break;
234*7c478bd9Sstevel@tonic-gate 		size = realsize;
235*7c478bd9Sstevel@tonic-gate 		size = ksyms_buflist_alloc(&hdr, size);
236*7c478bd9Sstevel@tonic-gate 		if (size == 0)
237*7c478bd9Sstevel@tonic-gate 			return (ENOMEM);
238*7c478bd9Sstevel@tonic-gate 		hptr = (void *)&hdr;
239*7c478bd9Sstevel@tonic-gate 	}
240*7c478bd9Sstevel@tonic-gate 
241*7c478bd9Sstevel@tonic-gate 	addr = ksyms_mapin(&hdr, realsize);
242*7c478bd9Sstevel@tonic-gate 	ksyms_buflist_free(&hdr);
243*7c478bd9Sstevel@tonic-gate 	if (addr == NULL)
244*7c478bd9Sstevel@tonic-gate 		return (EOVERFLOW);
245*7c478bd9Sstevel@tonic-gate 
246*7c478bd9Sstevel@tonic-gate 	/*
247*7c478bd9Sstevel@tonic-gate 	 * Reserve a clone entry.  Note that we don't use clone 0
248*7c478bd9Sstevel@tonic-gate 	 * since that's the "real" minor number.
249*7c478bd9Sstevel@tonic-gate 	 */
250*7c478bd9Sstevel@tonic-gate 	for (clone = 1; clone < nksyms_clones; clone++) {
251*7c478bd9Sstevel@tonic-gate 		if (casptr(&ksyms_clones[clone].ksyms_base, 0, addr) == 0) {
252*7c478bd9Sstevel@tonic-gate 			ksyms_clones[clone].ksyms_size = realsize;
253*7c478bd9Sstevel@tonic-gate 			*devp = makedevice(getemajor(*devp), clone);
254*7c478bd9Sstevel@tonic-gate 			(void) ddi_prop_update_int(*devp, ksyms_devi,
255*7c478bd9Sstevel@tonic-gate 			    "size", realsize);
256*7c478bd9Sstevel@tonic-gate 			modunload_disable();
257*7c478bd9Sstevel@tonic-gate 			return (0);
258*7c478bd9Sstevel@tonic-gate 		}
259*7c478bd9Sstevel@tonic-gate 	}
260*7c478bd9Sstevel@tonic-gate 	cmn_err(CE_NOTE, "ksyms: too many open references");
261*7c478bd9Sstevel@tonic-gate 	(void) as_unmap(curproc->p_as, addr, roundup(realsize, PAGESIZE));
262*7c478bd9Sstevel@tonic-gate 	return (ENXIO);
263*7c478bd9Sstevel@tonic-gate }
264*7c478bd9Sstevel@tonic-gate 
265*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
266*7c478bd9Sstevel@tonic-gate static int
267*7c478bd9Sstevel@tonic-gate ksyms_close(dev_t dev, int flag, int otyp, struct cred *cred)
268*7c478bd9Sstevel@tonic-gate {
269*7c478bd9Sstevel@tonic-gate 	minor_t clone = getminor(dev);
270*7c478bd9Sstevel@tonic-gate 
271*7c478bd9Sstevel@tonic-gate 	(void) as_unmap(curproc->p_as, ksyms_clones[clone].ksyms_base,
272*7c478bd9Sstevel@tonic-gate 	    roundup(ksyms_clones[clone].ksyms_size, PAGESIZE));
273*7c478bd9Sstevel@tonic-gate 	ksyms_clones[clone].ksyms_base = 0;
274*7c478bd9Sstevel@tonic-gate 	modunload_enable();
275*7c478bd9Sstevel@tonic-gate 	(void) ddi_prop_remove(dev, ksyms_devi, "size");
276*7c478bd9Sstevel@tonic-gate 	return (0);
277*7c478bd9Sstevel@tonic-gate }
278*7c478bd9Sstevel@tonic-gate 
279*7c478bd9Sstevel@tonic-gate static int
280*7c478bd9Sstevel@tonic-gate ksyms_symtbl_copy(ksyms_image_t *kip, struct uio *uio, size_t len)
281*7c478bd9Sstevel@tonic-gate {
282*7c478bd9Sstevel@tonic-gate 	char *buf;
283*7c478bd9Sstevel@tonic-gate 	int error = 0;
284*7c478bd9Sstevel@tonic-gate 	caddr_t base;
285*7c478bd9Sstevel@tonic-gate 	off_t off = uio->uio_offset;
286*7c478bd9Sstevel@tonic-gate 	size_t size;
287*7c478bd9Sstevel@tonic-gate 
288*7c478bd9Sstevel@tonic-gate 	/*
289*7c478bd9Sstevel@tonic-gate 	 * The symbol table is stored in the user address space,
290*7c478bd9Sstevel@tonic-gate 	 * so we have to copy it into the kernel first,
291*7c478bd9Sstevel@tonic-gate 	 * then copy it back out to the specified user address.
292*7c478bd9Sstevel@tonic-gate 	 */
293*7c478bd9Sstevel@tonic-gate 	buf = kmem_alloc(PAGESIZE, KM_SLEEP);
294*7c478bd9Sstevel@tonic-gate 	base = kip->ksyms_base + off;
295*7c478bd9Sstevel@tonic-gate 	while (len) {
296*7c478bd9Sstevel@tonic-gate 		size = MIN(PAGESIZE, len);
297*7c478bd9Sstevel@tonic-gate 		if (copyin(base, buf, size))
298*7c478bd9Sstevel@tonic-gate 			error = EFAULT;
299*7c478bd9Sstevel@tonic-gate 		else
300*7c478bd9Sstevel@tonic-gate 			error = uiomove(buf, size, UIO_READ, uio);
301*7c478bd9Sstevel@tonic-gate 
302*7c478bd9Sstevel@tonic-gate 		if (error)
303*7c478bd9Sstevel@tonic-gate 			break;
304*7c478bd9Sstevel@tonic-gate 
305*7c478bd9Sstevel@tonic-gate 		len -= size;
306*7c478bd9Sstevel@tonic-gate 		base += size;
307*7c478bd9Sstevel@tonic-gate 	}
308*7c478bd9Sstevel@tonic-gate 	kmem_free(buf, PAGESIZE);
309*7c478bd9Sstevel@tonic-gate 	return (error);
310*7c478bd9Sstevel@tonic-gate }
311*7c478bd9Sstevel@tonic-gate 
312*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
313*7c478bd9Sstevel@tonic-gate static int
314*7c478bd9Sstevel@tonic-gate ksyms_read(dev_t dev, struct uio *uio, struct cred *cred)
315*7c478bd9Sstevel@tonic-gate {
316*7c478bd9Sstevel@tonic-gate 	ksyms_image_t *kip = &ksyms_clones[getminor(dev)];
317*7c478bd9Sstevel@tonic-gate 	off_t off = uio->uio_offset;
318*7c478bd9Sstevel@tonic-gate 	size_t len = uio->uio_resid;
319*7c478bd9Sstevel@tonic-gate 
320*7c478bd9Sstevel@tonic-gate 	if (off < 0 || off > kip->ksyms_size)
321*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
322*7c478bd9Sstevel@tonic-gate 
323*7c478bd9Sstevel@tonic-gate 	if (len > kip->ksyms_size - off)
324*7c478bd9Sstevel@tonic-gate 		len = kip->ksyms_size - off;
325*7c478bd9Sstevel@tonic-gate 
326*7c478bd9Sstevel@tonic-gate 	if (len == 0)
327*7c478bd9Sstevel@tonic-gate 		return (0);
328*7c478bd9Sstevel@tonic-gate 
329*7c478bd9Sstevel@tonic-gate 	return (ksyms_symtbl_copy(kip, uio, len));
330*7c478bd9Sstevel@tonic-gate }
331*7c478bd9Sstevel@tonic-gate 
332*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
333*7c478bd9Sstevel@tonic-gate static int
334*7c478bd9Sstevel@tonic-gate ksyms_segmap(dev_t dev, off_t off, struct as *as, caddr_t *addrp, off_t len,
335*7c478bd9Sstevel@tonic-gate     uint_t prot, uint_t maxprot, uint_t flags, struct cred *cred)
336*7c478bd9Sstevel@tonic-gate {
337*7c478bd9Sstevel@tonic-gate 	ksyms_image_t *kip = &ksyms_clones[getminor(dev)];
338*7c478bd9Sstevel@tonic-gate 	int error = 0;
339*7c478bd9Sstevel@tonic-gate 	char *addr = NULL;
340*7c478bd9Sstevel@tonic-gate 	size_t rlen = 0;
341*7c478bd9Sstevel@tonic-gate 	struct iovec aiov;
342*7c478bd9Sstevel@tonic-gate 	struct uio auio;
343*7c478bd9Sstevel@tonic-gate 
344*7c478bd9Sstevel@tonic-gate 	if (flags & MAP_FIXED)
345*7c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
346*7c478bd9Sstevel@tonic-gate 
347*7c478bd9Sstevel@tonic-gate 	if (off < 0 || len <= 0 || off > kip->ksyms_size ||
348*7c478bd9Sstevel@tonic-gate 		len > kip->ksyms_size - off)
349*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate 	rlen = roundup(len, PAGESIZE);
352*7c478bd9Sstevel@tonic-gate 	if ((addr = ksyms_asmap(as, rlen)) == NULL)
353*7c478bd9Sstevel@tonic-gate 		return (EOVERFLOW);
354*7c478bd9Sstevel@tonic-gate 
355*7c478bd9Sstevel@tonic-gate 	aiov.iov_base = addr;
356*7c478bd9Sstevel@tonic-gate 	aiov.iov_len = len;
357*7c478bd9Sstevel@tonic-gate 	auio.uio_offset = off;
358*7c478bd9Sstevel@tonic-gate 	auio.uio_iov = &aiov;
359*7c478bd9Sstevel@tonic-gate 	auio.uio_iovcnt = 1;
360*7c478bd9Sstevel@tonic-gate 	auio.uio_resid = len;
361*7c478bd9Sstevel@tonic-gate 	auio.uio_segflg = UIO_USERSPACE;
362*7c478bd9Sstevel@tonic-gate 	auio.uio_llimit = MAXOFFSET_T;
363*7c478bd9Sstevel@tonic-gate 	auio.uio_fmode = FREAD;
364*7c478bd9Sstevel@tonic-gate 	auio.uio_extflg = UIO_COPY_CACHED;
365*7c478bd9Sstevel@tonic-gate 
366*7c478bd9Sstevel@tonic-gate 	error = ksyms_symtbl_copy(kip, &auio, len);
367*7c478bd9Sstevel@tonic-gate 
368*7c478bd9Sstevel@tonic-gate 	if (error)
369*7c478bd9Sstevel@tonic-gate 		(void) as_unmap(as, addr, rlen);
370*7c478bd9Sstevel@tonic-gate 	else
371*7c478bd9Sstevel@tonic-gate 		*addrp = addr;
372*7c478bd9Sstevel@tonic-gate 	return (error);
373*7c478bd9Sstevel@tonic-gate }
374*7c478bd9Sstevel@tonic-gate 
375*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
376*7c478bd9Sstevel@tonic-gate static int
377*7c478bd9Sstevel@tonic-gate ksyms_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
378*7c478bd9Sstevel@tonic-gate {
379*7c478bd9Sstevel@tonic-gate 	switch (infocmd) {
380*7c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
381*7c478bd9Sstevel@tonic-gate 		*result = ksyms_devi;
382*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
383*7c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
384*7c478bd9Sstevel@tonic-gate 		*result = 0;
385*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
386*7c478bd9Sstevel@tonic-gate 	}
387*7c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
388*7c478bd9Sstevel@tonic-gate }
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate static int
391*7c478bd9Sstevel@tonic-gate ksyms_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
392*7c478bd9Sstevel@tonic-gate {
393*7c478bd9Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
394*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
395*7c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "ksyms", S_IFCHR, 0, DDI_PSEUDO, NULL)
396*7c478bd9Sstevel@tonic-gate 	    == DDI_FAILURE) {
397*7c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(devi, NULL);
398*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
399*7c478bd9Sstevel@tonic-gate 	}
400*7c478bd9Sstevel@tonic-gate 	ksyms_devi = devi;
401*7c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
402*7c478bd9Sstevel@tonic-gate }
403*7c478bd9Sstevel@tonic-gate 
404*7c478bd9Sstevel@tonic-gate static int
405*7c478bd9Sstevel@tonic-gate ksyms_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
406*7c478bd9Sstevel@tonic-gate {
407*7c478bd9Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
408*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
409*7c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(devi, NULL);
410*7c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
411*7c478bd9Sstevel@tonic-gate }
412*7c478bd9Sstevel@tonic-gate 
413*7c478bd9Sstevel@tonic-gate static struct cb_ops ksyms_cb_ops = {
414*7c478bd9Sstevel@tonic-gate 	ksyms_open,		/* open */
415*7c478bd9Sstevel@tonic-gate 	ksyms_close,		/* close */
416*7c478bd9Sstevel@tonic-gate 	nodev,			/* strategy */
417*7c478bd9Sstevel@tonic-gate 	nodev,			/* print */
418*7c478bd9Sstevel@tonic-gate 	nodev,			/* dump */
419*7c478bd9Sstevel@tonic-gate 	ksyms_read,		/* read */
420*7c478bd9Sstevel@tonic-gate 	nodev,			/* write */
421*7c478bd9Sstevel@tonic-gate 	nodev,			/* ioctl */
422*7c478bd9Sstevel@tonic-gate 	nodev,			/* devmap */
423*7c478bd9Sstevel@tonic-gate 	nodev,			/* mmap */
424*7c478bd9Sstevel@tonic-gate 	ksyms_segmap,		/* segmap */
425*7c478bd9Sstevel@tonic-gate 	nochpoll,		/* poll */
426*7c478bd9Sstevel@tonic-gate 	ddi_prop_op,		/* prop_op */
427*7c478bd9Sstevel@tonic-gate 	0,			/* streamtab  */
428*7c478bd9Sstevel@tonic-gate 	D_NEW | D_MP		/* Driver compatibility flag */
429*7c478bd9Sstevel@tonic-gate };
430*7c478bd9Sstevel@tonic-gate 
431*7c478bd9Sstevel@tonic-gate static struct dev_ops ksyms_ops = {
432*7c478bd9Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev, */
433*7c478bd9Sstevel@tonic-gate 	0,			/* refcnt  */
434*7c478bd9Sstevel@tonic-gate 	ksyms_info,		/* info */
435*7c478bd9Sstevel@tonic-gate 	nulldev,		/* identify */
436*7c478bd9Sstevel@tonic-gate 	nulldev,		/* probe */
437*7c478bd9Sstevel@tonic-gate 	ksyms_attach,		/* attach */
438*7c478bd9Sstevel@tonic-gate 	ksyms_detach,		/* detach */
439*7c478bd9Sstevel@tonic-gate 	nodev,			/* reset */
440*7c478bd9Sstevel@tonic-gate 	&ksyms_cb_ops,		/* driver operations */
441*7c478bd9Sstevel@tonic-gate 	(struct bus_ops *)0	/* no bus operations */
442*7c478bd9Sstevel@tonic-gate };
443*7c478bd9Sstevel@tonic-gate 
444*7c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
445*7c478bd9Sstevel@tonic-gate 	&mod_driverops, "kernel symbols driver %I%", &ksyms_ops,
446*7c478bd9Sstevel@tonic-gate };
447*7c478bd9Sstevel@tonic-gate 
448*7c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
449*7c478bd9Sstevel@tonic-gate 	MODREV_1, { (void *)&modldrv }
450*7c478bd9Sstevel@tonic-gate };
451*7c478bd9Sstevel@tonic-gate 
452*7c478bd9Sstevel@tonic-gate int
453*7c478bd9Sstevel@tonic-gate _init(void)
454*7c478bd9Sstevel@tonic-gate {
455*7c478bd9Sstevel@tonic-gate 	int error;
456*7c478bd9Sstevel@tonic-gate 
457*7c478bd9Sstevel@tonic-gate 	if (nksyms_clones == 0)
458*7c478bd9Sstevel@tonic-gate 		nksyms_clones = maxusers + 50;
459*7c478bd9Sstevel@tonic-gate 
460*7c478bd9Sstevel@tonic-gate 	ksyms_clones = kmem_zalloc(nksyms_clones *
461*7c478bd9Sstevel@tonic-gate 	    sizeof (ksyms_image_t), KM_SLEEP);
462*7c478bd9Sstevel@tonic-gate 
463*7c478bd9Sstevel@tonic-gate 	if ((error = mod_install(&modlinkage)) != 0)
464*7c478bd9Sstevel@tonic-gate 		kmem_free(ksyms_clones, nksyms_clones * sizeof (ksyms_image_t));
465*7c478bd9Sstevel@tonic-gate 
466*7c478bd9Sstevel@tonic-gate 	return (error);
467*7c478bd9Sstevel@tonic-gate }
468*7c478bd9Sstevel@tonic-gate 
469*7c478bd9Sstevel@tonic-gate int
470*7c478bd9Sstevel@tonic-gate _fini(void)
471*7c478bd9Sstevel@tonic-gate {
472*7c478bd9Sstevel@tonic-gate 	int error;
473*7c478bd9Sstevel@tonic-gate 
474*7c478bd9Sstevel@tonic-gate 	if ((error = mod_remove(&modlinkage)) == 0)
475*7c478bd9Sstevel@tonic-gate 		kmem_free(ksyms_clones, nksyms_clones * sizeof (ksyms_image_t));
476*7c478bd9Sstevel@tonic-gate 	return (error);
477*7c478bd9Sstevel@tonic-gate }
478*7c478bd9Sstevel@tonic-gate 
479*7c478bd9Sstevel@tonic-gate int
480*7c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
481*7c478bd9Sstevel@tonic-gate {
482*7c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
483*7c478bd9Sstevel@tonic-gate }
484