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