10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
56036Smec * Common Development and Distribution License (the "License").
66036Smec * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
226036Smec * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate * ksyms driver - exports a single symbol/string table for the kernel
290Sstevel@tonic-gate * by concatenating all the module symbol/string tables.
300Sstevel@tonic-gate */
310Sstevel@tonic-gate
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/sysmacros.h>
340Sstevel@tonic-gate #include <sys/cmn_err.h>
350Sstevel@tonic-gate #include <sys/uio.h>
360Sstevel@tonic-gate #include <sys/kmem.h>
370Sstevel@tonic-gate #include <sys/cred.h>
380Sstevel@tonic-gate #include <sys/mman.h>
390Sstevel@tonic-gate #include <sys/errno.h>
400Sstevel@tonic-gate #include <sys/stat.h>
410Sstevel@tonic-gate #include <sys/conf.h>
420Sstevel@tonic-gate #include <sys/debug.h>
430Sstevel@tonic-gate #include <sys/kobj.h>
440Sstevel@tonic-gate #include <sys/ksyms.h>
450Sstevel@tonic-gate #include <sys/vmsystm.h>
460Sstevel@tonic-gate #include <vm/seg_vn.h>
470Sstevel@tonic-gate #include <sys/atomic.h>
480Sstevel@tonic-gate #include <sys/compress.h>
490Sstevel@tonic-gate #include <sys/ddi.h>
500Sstevel@tonic-gate #include <sys/sunddi.h>
510Sstevel@tonic-gate #include <sys/list.h>
520Sstevel@tonic-gate
530Sstevel@tonic-gate typedef struct ksyms_image {
540Sstevel@tonic-gate caddr_t ksyms_base; /* base address of image */
550Sstevel@tonic-gate size_t ksyms_size; /* size of image */
560Sstevel@tonic-gate } ksyms_image_t;
570Sstevel@tonic-gate
580Sstevel@tonic-gate typedef struct ksyms_buflist {
590Sstevel@tonic-gate list_node_t buflist_node;
600Sstevel@tonic-gate char buf[1];
610Sstevel@tonic-gate } ksyms_buflist_t;
620Sstevel@tonic-gate
630Sstevel@tonic-gate typedef struct ksyms_buflist_hdr {
640Sstevel@tonic-gate list_t blist;
650Sstevel@tonic-gate int nchunks;
660Sstevel@tonic-gate ksyms_buflist_t *cur;
670Sstevel@tonic-gate size_t curbuf_off;
680Sstevel@tonic-gate } ksyms_buflist_hdr_t;
690Sstevel@tonic-gate
700Sstevel@tonic-gate #define BUF_SIZE (PAGESIZE - (size_t)offsetof(ksyms_buflist_t, buf))
710Sstevel@tonic-gate
720Sstevel@tonic-gate int nksyms_clones; /* tunable: max clones of this device */
730Sstevel@tonic-gate
740Sstevel@tonic-gate static ksyms_image_t *ksyms_clones; /* clone device array */
750Sstevel@tonic-gate static dev_info_t *ksyms_devi;
760Sstevel@tonic-gate
770Sstevel@tonic-gate static void
ksyms_bcopy(const void * srcptr,void * ptr,size_t rsize)780Sstevel@tonic-gate ksyms_bcopy(const void *srcptr, void *ptr, size_t rsize)
790Sstevel@tonic-gate {
800Sstevel@tonic-gate
810Sstevel@tonic-gate size_t sz;
820Sstevel@tonic-gate const char *src = (const char *)srcptr;
830Sstevel@tonic-gate ksyms_buflist_hdr_t *hptr = (ksyms_buflist_hdr_t *)ptr;
840Sstevel@tonic-gate
850Sstevel@tonic-gate if (hptr->cur == NULL)
860Sstevel@tonic-gate return;
870Sstevel@tonic-gate
880Sstevel@tonic-gate while (rsize) {
890Sstevel@tonic-gate sz = MIN(rsize, (BUF_SIZE - hptr->curbuf_off));
900Sstevel@tonic-gate bcopy(src, (hptr->cur->buf + hptr->curbuf_off), sz);
910Sstevel@tonic-gate
920Sstevel@tonic-gate hptr->curbuf_off += sz;
930Sstevel@tonic-gate if (hptr->curbuf_off == BUF_SIZE) {
940Sstevel@tonic-gate hptr->curbuf_off = 0;
950Sstevel@tonic-gate hptr->cur = list_next(&hptr->blist, hptr->cur);
960Sstevel@tonic-gate if (hptr->cur == NULL)
970Sstevel@tonic-gate break;
980Sstevel@tonic-gate }
990Sstevel@tonic-gate src += sz;
1000Sstevel@tonic-gate rsize -= sz;
1010Sstevel@tonic-gate }
1020Sstevel@tonic-gate }
1030Sstevel@tonic-gate
1040Sstevel@tonic-gate static void
ksyms_buflist_free(ksyms_buflist_hdr_t * hdr)1050Sstevel@tonic-gate ksyms_buflist_free(ksyms_buflist_hdr_t *hdr)
1060Sstevel@tonic-gate {
1070Sstevel@tonic-gate ksyms_buflist_t *list;
1080Sstevel@tonic-gate
1090Sstevel@tonic-gate while (list = list_head(&hdr->blist)) {
1100Sstevel@tonic-gate list_remove(&hdr->blist, list);
1110Sstevel@tonic-gate kmem_free(list, PAGESIZE);
1120Sstevel@tonic-gate }
1130Sstevel@tonic-gate list_destroy(&hdr->blist);
1140Sstevel@tonic-gate hdr->cur = NULL;
1150Sstevel@tonic-gate }
1160Sstevel@tonic-gate
1170Sstevel@tonic-gate
1180Sstevel@tonic-gate /*
1190Sstevel@tonic-gate * Allocate 'size'(rounded to BUF_SIZE) bytes in chunks of BUF_SIZE, and
1200Sstevel@tonic-gate * add it to the buf list.
1210Sstevel@tonic-gate * Returns the total size rounded to BUF_SIZE.
1220Sstevel@tonic-gate */
1230Sstevel@tonic-gate static size_t
ksyms_buflist_alloc(ksyms_buflist_hdr_t * hdr,size_t size)1240Sstevel@tonic-gate ksyms_buflist_alloc(ksyms_buflist_hdr_t *hdr, size_t size)
1250Sstevel@tonic-gate {
1260Sstevel@tonic-gate int chunks, i;
1270Sstevel@tonic-gate ksyms_buflist_t *list;
1280Sstevel@tonic-gate
1290Sstevel@tonic-gate chunks = howmany(size, BUF_SIZE);
1300Sstevel@tonic-gate
1310Sstevel@tonic-gate if (hdr->nchunks >= chunks)
1320Sstevel@tonic-gate return (hdr->nchunks * BUF_SIZE);
1330Sstevel@tonic-gate
1340Sstevel@tonic-gate /*
1350Sstevel@tonic-gate * Allocate chunks - hdr->nchunks buffers and add them to
1360Sstevel@tonic-gate * the list.
1370Sstevel@tonic-gate */
1380Sstevel@tonic-gate for (i = chunks - hdr->nchunks; i > 0; i--) {
1390Sstevel@tonic-gate
1400Sstevel@tonic-gate if ((list = kmem_alloc(PAGESIZE, KM_NOSLEEP)) == NULL)
1410Sstevel@tonic-gate break;
1420Sstevel@tonic-gate
1430Sstevel@tonic-gate list_insert_tail(&hdr->blist, list);
1440Sstevel@tonic-gate }
1450Sstevel@tonic-gate
1460Sstevel@tonic-gate /*
1470Sstevel@tonic-gate * If we are running short of memory, free memory allocated till now
1480Sstevel@tonic-gate * and return.
1490Sstevel@tonic-gate */
1500Sstevel@tonic-gate if (i > 0) {
1510Sstevel@tonic-gate ksyms_buflist_free(hdr);
1520Sstevel@tonic-gate return (0);
1530Sstevel@tonic-gate }
1540Sstevel@tonic-gate
1550Sstevel@tonic-gate hdr->nchunks = chunks;
1560Sstevel@tonic-gate hdr->cur = list_head(&hdr->blist);
1570Sstevel@tonic-gate hdr->curbuf_off = 0;
1580Sstevel@tonic-gate
1590Sstevel@tonic-gate return (chunks * BUF_SIZE);
1600Sstevel@tonic-gate }
1610Sstevel@tonic-gate
1620Sstevel@tonic-gate /*
1630Sstevel@tonic-gate * rlen is in multiples of PAGESIZE
1640Sstevel@tonic-gate */
1650Sstevel@tonic-gate static char *
ksyms_asmap(struct as * as,size_t rlen)1660Sstevel@tonic-gate ksyms_asmap(struct as *as, size_t rlen)
1670Sstevel@tonic-gate {
1686036Smec char *addr = NULL;
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate as_rangelock(as);
1710Sstevel@tonic-gate map_addr(&addr, rlen, 0, 1, 0);
1720Sstevel@tonic-gate if (addr == NULL || as_map(as, addr, rlen, segvn_create, zfod_argsp)) {
1730Sstevel@tonic-gate as_rangeunlock(as);
1740Sstevel@tonic-gate return (NULL);
1750Sstevel@tonic-gate }
1760Sstevel@tonic-gate as_rangeunlock(as);
1770Sstevel@tonic-gate return (addr);
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate
1800Sstevel@tonic-gate static char *
ksyms_mapin(ksyms_buflist_hdr_t * hdr,size_t size)1810Sstevel@tonic-gate ksyms_mapin(ksyms_buflist_hdr_t *hdr, size_t size)
1820Sstevel@tonic-gate {
1830Sstevel@tonic-gate size_t sz, rlen = roundup(size, PAGESIZE);
1840Sstevel@tonic-gate struct as *as = curproc->p_as;
1850Sstevel@tonic-gate char *addr, *raddr;
1860Sstevel@tonic-gate ksyms_buflist_t *list = list_head(&hdr->blist);
1870Sstevel@tonic-gate
1880Sstevel@tonic-gate if ((addr = ksyms_asmap(as, rlen)) == NULL)
1890Sstevel@tonic-gate return (NULL);
1900Sstevel@tonic-gate
1910Sstevel@tonic-gate raddr = addr;
1920Sstevel@tonic-gate while (size > 0 && list != NULL) {
1930Sstevel@tonic-gate sz = MIN(size, BUF_SIZE);
1940Sstevel@tonic-gate
1950Sstevel@tonic-gate if (copyout(list->buf, raddr, sz)) {
1960Sstevel@tonic-gate (void) as_unmap(as, addr, rlen);
1970Sstevel@tonic-gate return (NULL);
1980Sstevel@tonic-gate }
1990Sstevel@tonic-gate list = list_next(&hdr->blist, list);
2000Sstevel@tonic-gate raddr += sz;
2010Sstevel@tonic-gate size -= sz;
2020Sstevel@tonic-gate }
2030Sstevel@tonic-gate return (addr);
2040Sstevel@tonic-gate }
2050Sstevel@tonic-gate
2060Sstevel@tonic-gate /*
2070Sstevel@tonic-gate * Copy a snapshot of the kernel symbol table into the user's address space.
2080Sstevel@tonic-gate * The symbol table is copied in fragments so that we do not have to
2090Sstevel@tonic-gate * do a large kmem_alloc() which could fail/block if the kernel memory is
2100Sstevel@tonic-gate * fragmented.
2110Sstevel@tonic-gate */
2120Sstevel@tonic-gate /* ARGSUSED */
2130Sstevel@tonic-gate static int
ksyms_open(dev_t * devp,int flag,int otyp,struct cred * cred)2140Sstevel@tonic-gate ksyms_open(dev_t *devp, int flag, int otyp, struct cred *cred)
2150Sstevel@tonic-gate {
2160Sstevel@tonic-gate minor_t clone;
2170Sstevel@tonic-gate size_t size = 0;
2180Sstevel@tonic-gate size_t realsize;
2190Sstevel@tonic-gate char *addr;
2200Sstevel@tonic-gate void *hptr = NULL;
2210Sstevel@tonic-gate ksyms_buflist_hdr_t hdr;
2220Sstevel@tonic-gate bzero(&hdr, sizeof (struct ksyms_buflist_hdr));
2230Sstevel@tonic-gate list_create(&hdr.blist, PAGESIZE,
2246036Smec offsetof(ksyms_buflist_t, buflist_node));
2250Sstevel@tonic-gate
2266731Scth if (getminor(*devp) != 0)
2276731Scth return (ENXIO);
2280Sstevel@tonic-gate
2290Sstevel@tonic-gate for (;;) {
2300Sstevel@tonic-gate realsize = ksyms_snapshot(ksyms_bcopy, hptr, size);
2310Sstevel@tonic-gate if (realsize <= size)
2320Sstevel@tonic-gate break;
2330Sstevel@tonic-gate size = realsize;
2340Sstevel@tonic-gate size = ksyms_buflist_alloc(&hdr, size);
2350Sstevel@tonic-gate if (size == 0)
2360Sstevel@tonic-gate return (ENOMEM);
2370Sstevel@tonic-gate hptr = (void *)&hdr;
2380Sstevel@tonic-gate }
2390Sstevel@tonic-gate
2400Sstevel@tonic-gate addr = ksyms_mapin(&hdr, realsize);
2410Sstevel@tonic-gate ksyms_buflist_free(&hdr);
2420Sstevel@tonic-gate if (addr == NULL)
2430Sstevel@tonic-gate return (EOVERFLOW);
2440Sstevel@tonic-gate
2450Sstevel@tonic-gate /*
2460Sstevel@tonic-gate * Reserve a clone entry. Note that we don't use clone 0
2470Sstevel@tonic-gate * since that's the "real" minor number.
2480Sstevel@tonic-gate */
2490Sstevel@tonic-gate for (clone = 1; clone < nksyms_clones; clone++) {
2500Sstevel@tonic-gate if (casptr(&ksyms_clones[clone].ksyms_base, 0, addr) == 0) {
2510Sstevel@tonic-gate ksyms_clones[clone].ksyms_size = realsize;
2520Sstevel@tonic-gate *devp = makedevice(getemajor(*devp), clone);
2530Sstevel@tonic-gate (void) ddi_prop_update_int(*devp, ksyms_devi,
2540Sstevel@tonic-gate "size", realsize);
2550Sstevel@tonic-gate modunload_disable();
2560Sstevel@tonic-gate return (0);
2570Sstevel@tonic-gate }
2580Sstevel@tonic-gate }
2590Sstevel@tonic-gate cmn_err(CE_NOTE, "ksyms: too many open references");
2600Sstevel@tonic-gate (void) as_unmap(curproc->p_as, addr, roundup(realsize, PAGESIZE));
2610Sstevel@tonic-gate return (ENXIO);
2620Sstevel@tonic-gate }
2630Sstevel@tonic-gate
2640Sstevel@tonic-gate /* ARGSUSED */
2650Sstevel@tonic-gate static int
ksyms_close(dev_t dev,int flag,int otyp,struct cred * cred)2660Sstevel@tonic-gate ksyms_close(dev_t dev, int flag, int otyp, struct cred *cred)
2670Sstevel@tonic-gate {
2680Sstevel@tonic-gate minor_t clone = getminor(dev);
2690Sstevel@tonic-gate
2700Sstevel@tonic-gate (void) as_unmap(curproc->p_as, ksyms_clones[clone].ksyms_base,
2710Sstevel@tonic-gate roundup(ksyms_clones[clone].ksyms_size, PAGESIZE));
2720Sstevel@tonic-gate ksyms_clones[clone].ksyms_base = 0;
2730Sstevel@tonic-gate modunload_enable();
2740Sstevel@tonic-gate (void) ddi_prop_remove(dev, ksyms_devi, "size");
2750Sstevel@tonic-gate return (0);
2760Sstevel@tonic-gate }
2770Sstevel@tonic-gate
2780Sstevel@tonic-gate static int
ksyms_symtbl_copy(ksyms_image_t * kip,struct uio * uio,size_t len)2790Sstevel@tonic-gate ksyms_symtbl_copy(ksyms_image_t *kip, struct uio *uio, size_t len)
2800Sstevel@tonic-gate {
2810Sstevel@tonic-gate char *buf;
2820Sstevel@tonic-gate int error = 0;
2830Sstevel@tonic-gate caddr_t base;
2840Sstevel@tonic-gate off_t off = uio->uio_offset;
2850Sstevel@tonic-gate size_t size;
2860Sstevel@tonic-gate
2870Sstevel@tonic-gate /*
2880Sstevel@tonic-gate * The symbol table is stored in the user address space,
2890Sstevel@tonic-gate * so we have to copy it into the kernel first,
2900Sstevel@tonic-gate * then copy it back out to the specified user address.
2910Sstevel@tonic-gate */
2920Sstevel@tonic-gate buf = kmem_alloc(PAGESIZE, KM_SLEEP);
2930Sstevel@tonic-gate base = kip->ksyms_base + off;
2940Sstevel@tonic-gate while (len) {
2950Sstevel@tonic-gate size = MIN(PAGESIZE, len);
2960Sstevel@tonic-gate if (copyin(base, buf, size))
2970Sstevel@tonic-gate error = EFAULT;
2980Sstevel@tonic-gate else
2990Sstevel@tonic-gate error = uiomove(buf, size, UIO_READ, uio);
3000Sstevel@tonic-gate
3010Sstevel@tonic-gate if (error)
3020Sstevel@tonic-gate break;
3030Sstevel@tonic-gate
3040Sstevel@tonic-gate len -= size;
3050Sstevel@tonic-gate base += size;
3060Sstevel@tonic-gate }
3070Sstevel@tonic-gate kmem_free(buf, PAGESIZE);
3080Sstevel@tonic-gate return (error);
3090Sstevel@tonic-gate }
3100Sstevel@tonic-gate
3110Sstevel@tonic-gate /* ARGSUSED */
3120Sstevel@tonic-gate static int
ksyms_read(dev_t dev,struct uio * uio,struct cred * cred)3130Sstevel@tonic-gate ksyms_read(dev_t dev, struct uio *uio, struct cred *cred)
3140Sstevel@tonic-gate {
3150Sstevel@tonic-gate ksyms_image_t *kip = &ksyms_clones[getminor(dev)];
3160Sstevel@tonic-gate off_t off = uio->uio_offset;
3170Sstevel@tonic-gate size_t len = uio->uio_resid;
3180Sstevel@tonic-gate
3190Sstevel@tonic-gate if (off < 0 || off > kip->ksyms_size)
3200Sstevel@tonic-gate return (EFAULT);
3210Sstevel@tonic-gate
3220Sstevel@tonic-gate if (len > kip->ksyms_size - off)
3230Sstevel@tonic-gate len = kip->ksyms_size - off;
3240Sstevel@tonic-gate
3250Sstevel@tonic-gate if (len == 0)
3260Sstevel@tonic-gate return (0);
3270Sstevel@tonic-gate
3280Sstevel@tonic-gate return (ksyms_symtbl_copy(kip, uio, len));
3290Sstevel@tonic-gate }
3300Sstevel@tonic-gate
3310Sstevel@tonic-gate /* ARGSUSED */
3320Sstevel@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)3330Sstevel@tonic-gate ksyms_segmap(dev_t dev, off_t off, struct as *as, caddr_t *addrp, off_t len,
3340Sstevel@tonic-gate uint_t prot, uint_t maxprot, uint_t flags, struct cred *cred)
3350Sstevel@tonic-gate {
3360Sstevel@tonic-gate ksyms_image_t *kip = &ksyms_clones[getminor(dev)];
3370Sstevel@tonic-gate int error = 0;
3380Sstevel@tonic-gate char *addr = NULL;
3390Sstevel@tonic-gate size_t rlen = 0;
3400Sstevel@tonic-gate struct iovec aiov;
3410Sstevel@tonic-gate struct uio auio;
3420Sstevel@tonic-gate
3430Sstevel@tonic-gate if (flags & MAP_FIXED)
3440Sstevel@tonic-gate return (ENOTSUP);
3450Sstevel@tonic-gate
3460Sstevel@tonic-gate if (off < 0 || len <= 0 || off > kip->ksyms_size ||
3476036Smec len > kip->ksyms_size - off)
3480Sstevel@tonic-gate return (EINVAL);
3490Sstevel@tonic-gate
3500Sstevel@tonic-gate rlen = roundup(len, PAGESIZE);
3510Sstevel@tonic-gate if ((addr = ksyms_asmap(as, rlen)) == NULL)
3520Sstevel@tonic-gate return (EOVERFLOW);
3530Sstevel@tonic-gate
3540Sstevel@tonic-gate aiov.iov_base = addr;
3550Sstevel@tonic-gate aiov.iov_len = len;
3560Sstevel@tonic-gate auio.uio_offset = off;
3570Sstevel@tonic-gate auio.uio_iov = &aiov;
3580Sstevel@tonic-gate auio.uio_iovcnt = 1;
3590Sstevel@tonic-gate auio.uio_resid = len;
3600Sstevel@tonic-gate auio.uio_segflg = UIO_USERSPACE;
3610Sstevel@tonic-gate auio.uio_llimit = MAXOFFSET_T;
3620Sstevel@tonic-gate auio.uio_fmode = FREAD;
3630Sstevel@tonic-gate auio.uio_extflg = UIO_COPY_CACHED;
3640Sstevel@tonic-gate
3650Sstevel@tonic-gate error = ksyms_symtbl_copy(kip, &auio, len);
3660Sstevel@tonic-gate
3670Sstevel@tonic-gate if (error)
3680Sstevel@tonic-gate (void) as_unmap(as, addr, rlen);
3690Sstevel@tonic-gate else
3700Sstevel@tonic-gate *addrp = addr;
3710Sstevel@tonic-gate return (error);
3720Sstevel@tonic-gate }
3730Sstevel@tonic-gate
3740Sstevel@tonic-gate /* ARGSUSED */
3750Sstevel@tonic-gate static int
ksyms_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)3760Sstevel@tonic-gate ksyms_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
3770Sstevel@tonic-gate {
3780Sstevel@tonic-gate switch (infocmd) {
3790Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO:
3800Sstevel@tonic-gate *result = ksyms_devi;
3810Sstevel@tonic-gate return (DDI_SUCCESS);
3820Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE:
3830Sstevel@tonic-gate *result = 0;
3840Sstevel@tonic-gate return (DDI_SUCCESS);
3850Sstevel@tonic-gate }
3860Sstevel@tonic-gate return (DDI_FAILURE);
3870Sstevel@tonic-gate }
3880Sstevel@tonic-gate
3890Sstevel@tonic-gate static int
ksyms_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)3900Sstevel@tonic-gate ksyms_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
3910Sstevel@tonic-gate {
3920Sstevel@tonic-gate if (cmd != DDI_ATTACH)
3930Sstevel@tonic-gate return (DDI_FAILURE);
3940Sstevel@tonic-gate if (ddi_create_minor_node(devi, "ksyms", S_IFCHR, 0, DDI_PSEUDO, NULL)
3950Sstevel@tonic-gate == DDI_FAILURE) {
3960Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL);
3970Sstevel@tonic-gate return (DDI_FAILURE);
3980Sstevel@tonic-gate }
3990Sstevel@tonic-gate ksyms_devi = devi;
4000Sstevel@tonic-gate return (DDI_SUCCESS);
4010Sstevel@tonic-gate }
4020Sstevel@tonic-gate
4030Sstevel@tonic-gate static int
ksyms_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)4040Sstevel@tonic-gate ksyms_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
4050Sstevel@tonic-gate {
4060Sstevel@tonic-gate if (cmd != DDI_DETACH)
4070Sstevel@tonic-gate return (DDI_FAILURE);
4080Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL);
4090Sstevel@tonic-gate return (DDI_SUCCESS);
4100Sstevel@tonic-gate }
4110Sstevel@tonic-gate
4120Sstevel@tonic-gate static struct cb_ops ksyms_cb_ops = {
4130Sstevel@tonic-gate ksyms_open, /* open */
4140Sstevel@tonic-gate ksyms_close, /* close */
4150Sstevel@tonic-gate nodev, /* strategy */
4160Sstevel@tonic-gate nodev, /* print */
4170Sstevel@tonic-gate nodev, /* dump */
4180Sstevel@tonic-gate ksyms_read, /* read */
4190Sstevel@tonic-gate nodev, /* write */
4200Sstevel@tonic-gate nodev, /* ioctl */
4210Sstevel@tonic-gate nodev, /* devmap */
4220Sstevel@tonic-gate nodev, /* mmap */
4230Sstevel@tonic-gate ksyms_segmap, /* segmap */
4240Sstevel@tonic-gate nochpoll, /* poll */
4250Sstevel@tonic-gate ddi_prop_op, /* prop_op */
4260Sstevel@tonic-gate 0, /* streamtab */
4270Sstevel@tonic-gate D_NEW | D_MP /* Driver compatibility flag */
4280Sstevel@tonic-gate };
4290Sstevel@tonic-gate
4300Sstevel@tonic-gate static struct dev_ops ksyms_ops = {
4310Sstevel@tonic-gate DEVO_REV, /* devo_rev, */
4320Sstevel@tonic-gate 0, /* refcnt */
4330Sstevel@tonic-gate ksyms_info, /* info */
4340Sstevel@tonic-gate nulldev, /* identify */
4350Sstevel@tonic-gate nulldev, /* probe */
4360Sstevel@tonic-gate ksyms_attach, /* attach */
4370Sstevel@tonic-gate ksyms_detach, /* detach */
4380Sstevel@tonic-gate nodev, /* reset */
4390Sstevel@tonic-gate &ksyms_cb_ops, /* driver operations */
440*7656SSherry.Moore@Sun.COM (struct bus_ops *)0, /* no bus operations */
441*7656SSherry.Moore@Sun.COM NULL, /* power */
442*7656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */
4430Sstevel@tonic-gate };
4440Sstevel@tonic-gate
4450Sstevel@tonic-gate static struct modldrv modldrv = {
446*7656SSherry.Moore@Sun.COM &mod_driverops, "kernel symbols driver", &ksyms_ops,
4470Sstevel@tonic-gate };
4480Sstevel@tonic-gate
4490Sstevel@tonic-gate static struct modlinkage modlinkage = {
4500Sstevel@tonic-gate MODREV_1, { (void *)&modldrv }
4510Sstevel@tonic-gate };
4520Sstevel@tonic-gate
4530Sstevel@tonic-gate int
_init(void)4540Sstevel@tonic-gate _init(void)
4550Sstevel@tonic-gate {
4560Sstevel@tonic-gate int error;
4570Sstevel@tonic-gate
4580Sstevel@tonic-gate if (nksyms_clones == 0)
4590Sstevel@tonic-gate nksyms_clones = maxusers + 50;
4600Sstevel@tonic-gate
4610Sstevel@tonic-gate ksyms_clones = kmem_zalloc(nksyms_clones *
4620Sstevel@tonic-gate sizeof (ksyms_image_t), KM_SLEEP);
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate if ((error = mod_install(&modlinkage)) != 0)
4650Sstevel@tonic-gate kmem_free(ksyms_clones, nksyms_clones * sizeof (ksyms_image_t));
4660Sstevel@tonic-gate
4670Sstevel@tonic-gate return (error);
4680Sstevel@tonic-gate }
4690Sstevel@tonic-gate
4700Sstevel@tonic-gate int
_fini(void)4710Sstevel@tonic-gate _fini(void)
4720Sstevel@tonic-gate {
4730Sstevel@tonic-gate int error;
4740Sstevel@tonic-gate
4750Sstevel@tonic-gate if ((error = mod_remove(&modlinkage)) == 0)
4760Sstevel@tonic-gate kmem_free(ksyms_clones, nksyms_clones * sizeof (ksyms_image_t));
4770Sstevel@tonic-gate return (error);
4780Sstevel@tonic-gate }
4790Sstevel@tonic-gate
4800Sstevel@tonic-gate int
_info(struct modinfo * modinfop)4810Sstevel@tonic-gate _info(struct modinfo *modinfop)
4820Sstevel@tonic-gate {
4830Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
4840Sstevel@tonic-gate }
485