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 (atomic_cas_ptr(&ksyms_clones
[clone
].ksyms_base
, 0, addr
) ==
252 ksyms_clones
[clone
].ksyms_size
= realsize
;
253 *devp
= makedevice(getemajor(*devp
), clone
);
254 (void) ddi_prop_update_int(*devp
, ksyms_devi
,
260 cmn_err(CE_NOTE
, "ksyms: too many open references");
261 (void) as_unmap(curproc
->p_as
, addr
, roundup(realsize
, PAGESIZE
));
267 ksyms_close(dev_t dev
, int flag
, int otyp
, struct cred
*cred
)
269 minor_t clone
= getminor(dev
);
271 (void) as_unmap(curproc
->p_as
, ksyms_clones
[clone
].ksyms_base
,
272 roundup(ksyms_clones
[clone
].ksyms_size
, PAGESIZE
));
273 ksyms_clones
[clone
].ksyms_base
= 0;
275 (void) ddi_prop_remove(dev
, ksyms_devi
, "size");
280 ksyms_symtbl_copy(ksyms_image_t
*kip
, struct uio
*uio
, size_t len
)
285 off_t off
= uio
->uio_offset
;
289 * The symbol table is stored in the user address space,
290 * so we have to copy it into the kernel first,
291 * then copy it back out to the specified user address.
293 buf
= kmem_alloc(PAGESIZE
, KM_SLEEP
);
294 base
= kip
->ksyms_base
+ off
;
296 size
= MIN(PAGESIZE
, len
);
297 if (copyin(base
, buf
, size
))
300 error
= uiomove(buf
, size
, UIO_READ
, uio
);
308 kmem_free(buf
, PAGESIZE
);
314 ksyms_read(dev_t dev
, struct uio
*uio
, struct cred
*cred
)
316 ksyms_image_t
*kip
= &ksyms_clones
[getminor(dev
)];
317 off_t off
= uio
->uio_offset
;
318 size_t len
= uio
->uio_resid
;
320 if (off
< 0 || off
> kip
->ksyms_size
)
323 if (len
> kip
->ksyms_size
- off
)
324 len
= kip
->ksyms_size
- off
;
329 return (ksyms_symtbl_copy(kip
, uio
, len
));
334 ksyms_segmap(dev_t dev
, off_t off
, struct as
*as
, caddr_t
*addrp
, off_t len
,
335 uint_t prot
, uint_t maxprot
, uint_t flags
, struct cred
*cred
)
337 ksyms_image_t
*kip
= &ksyms_clones
[getminor(dev
)];
344 if (flags
& MAP_FIXED
)
347 if (off
< 0 || len
<= 0 || off
> kip
->ksyms_size
||
348 len
> kip
->ksyms_size
- off
)
351 rlen
= roundup(len
, PAGESIZE
);
352 if ((addr
= ksyms_asmap(as
, rlen
)) == NULL
)
355 aiov
.iov_base
= addr
;
357 auio
.uio_offset
= off
;
358 auio
.uio_iov
= &aiov
;
360 auio
.uio_resid
= len
;
361 auio
.uio_segflg
= UIO_USERSPACE
;
362 auio
.uio_llimit
= MAXOFFSET_T
;
363 auio
.uio_fmode
= FREAD
;
364 auio
.uio_extflg
= UIO_COPY_CACHED
;
366 error
= ksyms_symtbl_copy(kip
, &auio
, len
);
369 (void) as_unmap(as
, addr
, rlen
);
377 ksyms_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
380 case DDI_INFO_DEVT2DEVINFO
:
381 *result
= ksyms_devi
;
382 return (DDI_SUCCESS
);
383 case DDI_INFO_DEVT2INSTANCE
:
385 return (DDI_SUCCESS
);
387 return (DDI_FAILURE
);
391 ksyms_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
393 if (cmd
!= DDI_ATTACH
)
394 return (DDI_FAILURE
);
395 if (ddi_create_minor_node(devi
, "ksyms", S_IFCHR
, 0, DDI_PSEUDO
, 0)
397 ddi_remove_minor_node(devi
, NULL
);
398 return (DDI_FAILURE
);
401 return (DDI_SUCCESS
);
405 ksyms_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
407 if (cmd
!= DDI_DETACH
)
408 return (DDI_FAILURE
);
409 ddi_remove_minor_node(devi
, NULL
);
410 return (DDI_SUCCESS
);
413 static struct cb_ops ksyms_cb_ops
= {
414 ksyms_open
, /* open */
415 ksyms_close
, /* close */
416 nodev
, /* strategy */
419 ksyms_read
, /* read */
424 ksyms_segmap
, /* segmap */
426 ddi_prop_op
, /* prop_op */
428 D_NEW
| D_MP
/* Driver compatibility flag */
431 static struct dev_ops ksyms_ops
= {
432 DEVO_REV
, /* devo_rev, */
434 ksyms_info
, /* info */
435 nulldev
, /* identify */
437 ksyms_attach
, /* attach */
438 ksyms_detach
, /* detach */
440 &ksyms_cb_ops
, /* driver operations */
441 NULL
, /* no bus operations */
443 ddi_quiesce_not_needed
, /* quiesce */
446 static struct modldrv modldrv
= {
447 &mod_driverops
, "kernel symbols driver", &ksyms_ops
,
450 static struct modlinkage modlinkage
= {
451 MODREV_1
, { (void *)&modldrv
}
459 if (nksyms_clones
== 0)
460 nksyms_clones
= maxusers
+ 50;
462 ksyms_clones
= kmem_zalloc(nksyms_clones
*
463 sizeof (ksyms_image_t
), KM_SLEEP
);
465 if ((error
= mod_install(&modlinkage
)) != 0)
466 kmem_free(ksyms_clones
, nksyms_clones
* sizeof (ksyms_image_t
));
476 if ((error
= mod_remove(&modlinkage
)) == 0)
477 kmem_free(ksyms_clones
, nksyms_clones
* sizeof (ksyms_image_t
));
482 _info(struct modinfo
*modinfop
)
484 return (mod_info(&modlinkage
, modinfop
));