4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 * ksyms driver - exports a single symbol/string table for the kernel
29 * by concatenating all the module symbol/string tables.
32 #include <sys/types.h>
33 #include <sys/sysmacros.h>
34 #include <sys/cmn_err.h>
39 #include <sys/errno.h>
42 #include <sys/debug.h>
44 #include <sys/ksyms.h>
45 #include <sys/vmsystm.h>
46 #include <vm/seg_vn.h>
47 #include <sys/atomic.h>
48 #include <sys/compress.h>
50 #include <sys/sunddi.h>
53 typedef struct ksyms_image
{
54 caddr_t ksyms_base
; /* base address of image */
55 size_t ksyms_size
; /* size of image */
58 typedef struct ksyms_buflist
{
59 list_node_t buflist_node
;
63 typedef struct ksyms_buflist_hdr
{
68 } ksyms_buflist_hdr_t
;
70 #define BUF_SIZE (PAGESIZE - (size_t)offsetof(ksyms_buflist_t, buf))
72 int nksyms_clones
; /* tunable: max clones of this device */
74 static ksyms_image_t
*ksyms_clones
; /* clone device array */
75 static dev_info_t
*ksyms_devi
;
78 ksyms_bcopy(const void *srcptr
, void *ptr
, size_t rsize
)
82 const char *src
= (const char *)srcptr
;
83 ksyms_buflist_hdr_t
*hptr
= (ksyms_buflist_hdr_t
*)ptr
;
85 if (hptr
->cur
== NULL
)
89 sz
= MIN(rsize
, (BUF_SIZE
- hptr
->curbuf_off
));
90 bcopy(src
, (hptr
->cur
->buf
+ hptr
->curbuf_off
), sz
);
92 hptr
->curbuf_off
+= sz
;
93 if (hptr
->curbuf_off
== BUF_SIZE
) {
95 hptr
->cur
= list_next(&hptr
->blist
, hptr
->cur
);
96 if (hptr
->cur
== NULL
)
105 ksyms_buflist_free(ksyms_buflist_hdr_t
*hdr
)
107 ksyms_buflist_t
*list
;
109 while (list
= list_head(&hdr
->blist
)) {
110 list_remove(&hdr
->blist
, list
);
111 kmem_free(list
, PAGESIZE
);
113 list_destroy(&hdr
->blist
);
119 * Allocate 'size'(rounded to BUF_SIZE) bytes in chunks of BUF_SIZE, and
120 * add it to the buf list.
121 * Returns the total size rounded to BUF_SIZE.
124 ksyms_buflist_alloc(ksyms_buflist_hdr_t
*hdr
, size_t size
)
127 ksyms_buflist_t
*list
;
129 chunks
= howmany(size
, BUF_SIZE
);
131 if (hdr
->nchunks
>= chunks
)
132 return (hdr
->nchunks
* BUF_SIZE
);
135 * Allocate chunks - hdr->nchunks buffers and add them to
138 for (i
= chunks
- hdr
->nchunks
; i
> 0; i
--) {
140 if ((list
= kmem_alloc(PAGESIZE
, KM_NOSLEEP
)) == NULL
)
143 list_insert_tail(&hdr
->blist
, list
);
147 * If we are running short of memory, free memory allocated till now
151 ksyms_buflist_free(hdr
);
155 hdr
->nchunks
= chunks
;
156 hdr
->cur
= list_head(&hdr
->blist
);
159 return (chunks
* BUF_SIZE
);
163 * rlen is in multiples of PAGESIZE
166 ksyms_asmap(struct as
*as
, size_t rlen
)
171 map_addr(&addr
, rlen
, 0, 1, 0);
172 if (addr
== NULL
|| as_map(as
, addr
, rlen
, segvn_create
, zfod_argsp
)) {
181 ksyms_mapin(ksyms_buflist_hdr_t
*hdr
, size_t size
)
183 size_t sz
, rlen
= roundup(size
, PAGESIZE
);
184 struct as
*as
= curproc
->p_as
;
186 ksyms_buflist_t
*list
= list_head(&hdr
->blist
);
188 if ((addr
= ksyms_asmap(as
, rlen
)) == NULL
)
192 while (size
> 0 && list
!= NULL
) {
193 sz
= MIN(size
, BUF_SIZE
);
195 if (copyout(list
->buf
, raddr
, sz
)) {
196 (void) as_unmap(as
, addr
, rlen
);
199 list
= list_next(&hdr
->blist
, list
);
207 * Copy a snapshot of the kernel symbol table into the user's address space.
208 * The symbol table is copied in fragments so that we do not have to
209 * do a large kmem_alloc() which could fail/block if the kernel memory is
214 ksyms_open(dev_t
*devp
, int flag
, int otyp
, struct cred
*cred
)
221 ksyms_buflist_hdr_t hdr
;
222 bzero(&hdr
, sizeof (struct ksyms_buflist_hdr
));
223 list_create(&hdr
.blist
, PAGESIZE
,
224 offsetof(ksyms_buflist_t
, buflist_node
));
226 if (getminor(*devp
) != 0)
230 realsize
= ksyms_snapshot(ksyms_bcopy
, hptr
, size
);
231 if (realsize
<= size
)
234 size
= ksyms_buflist_alloc(&hdr
, size
);
240 addr
= ksyms_mapin(&hdr
, realsize
);
241 ksyms_buflist_free(&hdr
);
246 * Reserve a clone entry. Note that we don't use clone 0
247 * since that's the "real" minor number.
249 for (clone
= 1; clone
< nksyms_clones
; clone
++) {
250 if (casptr(&ksyms_clones
[clone
].ksyms_base
, 0, addr
) == 0) {
251 ksyms_clones
[clone
].ksyms_size
= realsize
;
252 *devp
= makedevice(getemajor(*devp
), clone
);
253 (void) ddi_prop_update_int(*devp
, ksyms_devi
,
259 cmn_err(CE_NOTE
, "ksyms: too many open references");
260 (void) as_unmap(curproc
->p_as
, addr
, roundup(realsize
, PAGESIZE
));
266 ksyms_close(dev_t dev
, int flag
, int otyp
, struct cred
*cred
)
268 minor_t clone
= getminor(dev
);
270 (void) as_unmap(curproc
->p_as
, ksyms_clones
[clone
].ksyms_base
,
271 roundup(ksyms_clones
[clone
].ksyms_size
, PAGESIZE
));
272 ksyms_clones
[clone
].ksyms_base
= 0;
274 (void) ddi_prop_remove(dev
, ksyms_devi
, "size");
279 ksyms_symtbl_copy(ksyms_image_t
*kip
, struct uio
*uio
, size_t len
)
284 off_t off
= uio
->uio_offset
;
288 * The symbol table is stored in the user address space,
289 * so we have to copy it into the kernel first,
290 * then copy it back out to the specified user address.
292 buf
= kmem_alloc(PAGESIZE
, KM_SLEEP
);
293 base
= kip
->ksyms_base
+ off
;
295 size
= MIN(PAGESIZE
, len
);
296 if (copyin(base
, buf
, size
))
299 error
= uiomove(buf
, size
, UIO_READ
, uio
);
307 kmem_free(buf
, PAGESIZE
);
313 ksyms_read(dev_t dev
, struct uio
*uio
, struct cred
*cred
)
315 ksyms_image_t
*kip
= &ksyms_clones
[getminor(dev
)];
316 off_t off
= uio
->uio_offset
;
317 size_t len
= uio
->uio_resid
;
319 if (off
< 0 || off
> kip
->ksyms_size
)
322 if (len
> kip
->ksyms_size
- off
)
323 len
= kip
->ksyms_size
- off
;
328 return (ksyms_symtbl_copy(kip
, uio
, len
));
333 ksyms_segmap(dev_t dev
, off_t off
, struct as
*as
, caddr_t
*addrp
, off_t len
,
334 uint_t prot
, uint_t maxprot
, uint_t flags
, struct cred
*cred
)
336 ksyms_image_t
*kip
= &ksyms_clones
[getminor(dev
)];
343 if (flags
& MAP_FIXED
)
346 if (off
< 0 || len
<= 0 || off
> kip
->ksyms_size
||
347 len
> kip
->ksyms_size
- off
)
350 rlen
= roundup(len
, PAGESIZE
);
351 if ((addr
= ksyms_asmap(as
, rlen
)) == NULL
)
354 aiov
.iov_base
= addr
;
356 auio
.uio_offset
= off
;
357 auio
.uio_iov
= &aiov
;
359 auio
.uio_resid
= len
;
360 auio
.uio_segflg
= UIO_USERSPACE
;
361 auio
.uio_llimit
= MAXOFFSET_T
;
362 auio
.uio_fmode
= FREAD
;
363 auio
.uio_extflg
= UIO_COPY_CACHED
;
365 error
= ksyms_symtbl_copy(kip
, &auio
, len
);
368 (void) as_unmap(as
, addr
, rlen
);
376 ksyms_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
379 case DDI_INFO_DEVT2DEVINFO
:
380 *result
= ksyms_devi
;
381 return (DDI_SUCCESS
);
382 case DDI_INFO_DEVT2INSTANCE
:
384 return (DDI_SUCCESS
);
386 return (DDI_FAILURE
);
390 ksyms_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
392 if (cmd
!= DDI_ATTACH
)
393 return (DDI_FAILURE
);
394 if (ddi_create_minor_node(devi
, "ksyms", S_IFCHR
, 0, DDI_PSEUDO
, NULL
)
396 ddi_remove_minor_node(devi
, NULL
);
397 return (DDI_FAILURE
);
400 return (DDI_SUCCESS
);
404 ksyms_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
406 if (cmd
!= DDI_DETACH
)
407 return (DDI_FAILURE
);
408 ddi_remove_minor_node(devi
, NULL
);
409 return (DDI_SUCCESS
);
412 static struct cb_ops ksyms_cb_ops
= {
413 ksyms_open
, /* open */
414 ksyms_close
, /* close */
415 nodev
, /* strategy */
418 ksyms_read
, /* read */
423 ksyms_segmap
, /* segmap */
425 ddi_prop_op
, /* prop_op */
427 D_NEW
| D_MP
/* Driver compatibility flag */
430 static struct dev_ops ksyms_ops
= {
431 DEVO_REV
, /* devo_rev, */
433 ksyms_info
, /* info */
434 nulldev
, /* identify */
436 ksyms_attach
, /* attach */
437 ksyms_detach
, /* detach */
439 &ksyms_cb_ops
, /* driver operations */
440 (struct bus_ops
*)0, /* no bus operations */
442 ddi_quiesce_not_needed
, /* quiesce */
445 static struct modldrv modldrv
= {
446 &mod_driverops
, "kernel symbols driver", &ksyms_ops
,
449 static struct modlinkage modlinkage
= {
450 MODREV_1
, { (void *)&modldrv
}
458 if (nksyms_clones
== 0)
459 nksyms_clones
= maxusers
+ 50;
461 ksyms_clones
= kmem_zalloc(nksyms_clones
*
462 sizeof (ksyms_image_t
), KM_SLEEP
);
464 if ((error
= mod_install(&modlinkage
)) != 0)
465 kmem_free(ksyms_clones
, nksyms_clones
* sizeof (ksyms_image_t
));
475 if ((error
= mod_remove(&modlinkage
)) == 0)
476 kmem_free(ksyms_clones
, nksyms_clones
* sizeof (ksyms_image_t
));
481 _info(struct modinfo
*modinfop
)
483 return (mod_info(&modlinkage
, modinfop
));