1*437Smws /* 2*437Smws * CDDL HEADER START 3*437Smws * 4*437Smws * The contents of this file are subject to the terms of the 5*437Smws * Common Development and Distribution License, Version 1.0 only 6*437Smws * (the "License"). You may not use this file except in compliance 7*437Smws * with the License. 8*437Smws * 9*437Smws * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*437Smws * or http://www.opensolaris.org/os/licensing. 11*437Smws * See the License for the specific language governing permissions 12*437Smws * and limitations under the License. 13*437Smws * 14*437Smws * When distributing Covered Code, include this CDDL HEADER in each 15*437Smws * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*437Smws * If applicable, add the following below this CDDL HEADER, with the 17*437Smws * fields enclosed by brackets "[]" replaced with your own identifying 18*437Smws * information: Portions Copyright [yyyy] [name of copyright owner] 19*437Smws * 20*437Smws * CDDL HEADER END 21*437Smws */ 22*437Smws 23*437Smws /* 24*437Smws * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25*437Smws * Use is subject to license terms. 26*437Smws */ 27*437Smws 28*437Smws #pragma ident "%Z%%M% %I% %E% SMI" 29*437Smws 30*437Smws /* 31*437Smws * smbios(7D) driver 32*437Smws * 33*437Smws * This pseudo-driver makes available a snapshot of the system's SMBIOS image 34*437Smws * that can be accessed using libsmbios. Clients may access a snapshot using 35*437Smws * either read(2) or mmap(2). The driver returns the SMBIOS entry point data 36*437Smws * followed by the SMBIOS structure table. The entry point has its 'staddr' 37*437Smws * field set to indicate the byte offset of the structure table. The driver 38*437Smws * uses the common SMBIOS API defined in <sys/smbios.h> to access the image. 39*437Smws * 40*437Smws * At present, the kernel takes a single snapshot of SMBIOS at boot time and 41*437Smws * stores a handle for this snapshot in 'ksmbios'. To keep track of driver 42*437Smws * opens, we simply compare-and-swap this handle into an 'smb_clones' array. 43*437Smws * Future x86 systems may need to support dynamic SMBIOS updates: when that 44*437Smws * happens the SMBIOS API can be extended to support reference counting and 45*437Smws * handles for different snapshots can be stored in smb_clones[]. 46*437Smws */ 47*437Smws 48*437Smws #include <sys/smbios.h> 49*437Smws #include <sys/sysmacros.h> 50*437Smws #include <sys/cmn_err.h> 51*437Smws #include <sys/vmsystm.h> 52*437Smws #include <vm/seg_vn.h> 53*437Smws #include <sys/ddi.h> 54*437Smws #include <sys/sunddi.h> 55*437Smws #include <sys/modctl.h> 56*437Smws #include <sys/conf.h> 57*437Smws #include <sys/stat.h> 58*437Smws 59*437Smws typedef struct smb_clone { 60*437Smws smbios_hdl_t *c_hdl; 61*437Smws size_t c_eplen; 62*437Smws size_t c_stlen; 63*437Smws } smb_clone_t; 64*437Smws 65*437Smws static dev_info_t *smb_devi; 66*437Smws static smb_clone_t *smb_clones; 67*437Smws static int smb_nclones; 68*437Smws 69*437Smws /*ARGSUSED*/ 70*437Smws static int 71*437Smws smb_open(dev_t *dp, int flag, int otyp, cred_t *cred) 72*437Smws { 73*437Smws minor_t c; 74*437Smws 75*437Smws if (ksmbios == NULL) 76*437Smws return (ENXIO); 77*437Smws 78*437Smws /* 79*437Smws * Locate and reserve a clone structure. We skip clone 0 as that is 80*437Smws * the real minor number, and we assign a new minor to each clone. 81*437Smws */ 82*437Smws for (c = 1; c < smb_nclones; c++) { 83*437Smws if (casptr(&smb_clones[c].c_hdl, NULL, ksmbios) == NULL) 84*437Smws break; 85*437Smws } 86*437Smws 87*437Smws if (c >= smb_nclones) 88*437Smws return (EAGAIN); 89*437Smws 90*437Smws smb_clones[c].c_eplen = P2ROUNDUP(sizeof (smbios_entry_t), 16); 91*437Smws smb_clones[c].c_stlen = smbios_buflen(smb_clones[c].c_hdl); 92*437Smws 93*437Smws *dp = makedevice(getemajor(*dp), c); 94*437Smws 95*437Smws (void) ddi_prop_update_int(*dp, smb_devi, "size", 96*437Smws smb_clones[c].c_eplen + smb_clones[c].c_stlen); 97*437Smws 98*437Smws return (0); 99*437Smws } 100*437Smws 101*437Smws /*ARGSUSED*/ 102*437Smws static int 103*437Smws smb_close(dev_t dev, int flag, int otyp, cred_t *cred) 104*437Smws { 105*437Smws (void) ddi_prop_remove(dev, smb_devi, "size"); 106*437Smws smb_clones[getminor(dev)].c_hdl = NULL; 107*437Smws return (0); 108*437Smws } 109*437Smws 110*437Smws /* 111*437Smws * Common code to copy out the SMBIOS snapshot used for both read and mmap. 112*437Smws * The caller must validate uio_offset for us since semantics differ there. 113*437Smws * The copy is done in two stages, either of which can be skipped based on the 114*437Smws * offset and length: first we copy the entry point, with 'staddr' recalculated 115*437Smws * to indicate the offset of the data buffer, and second we copy the table. 116*437Smws */ 117*437Smws static int 118*437Smws smb_uiomove(smb_clone_t *cp, uio_t *uio) 119*437Smws { 120*437Smws off_t off = uio->uio_offset; 121*437Smws size_t len = uio->uio_resid; 122*437Smws int err = 0; 123*437Smws 124*437Smws if (off + len > cp->c_eplen + cp->c_stlen) 125*437Smws len = cp->c_eplen + cp->c_stlen - off; 126*437Smws 127*437Smws if (off < cp->c_eplen) { 128*437Smws smbios_entry_t *ep = kmem_zalloc(cp->c_eplen, KM_SLEEP); 129*437Smws size_t eprlen = MIN(len, cp->c_eplen - off); 130*437Smws 131*437Smws smbios_info_smbios(cp->c_hdl, ep); 132*437Smws ep->smbe_staddr = (uint32_t)cp->c_eplen; 133*437Smws smbios_checksum(cp->c_hdl, ep); 134*437Smws 135*437Smws err = uiomove((char *)ep + off, eprlen, UIO_READ, uio); 136*437Smws kmem_free(ep, cp->c_eplen); 137*437Smws 138*437Smws off += eprlen; 139*437Smws len -= eprlen; 140*437Smws } 141*437Smws 142*437Smws if (err == 0 && off >= cp->c_eplen) { 143*437Smws char *buf = (char *)smbios_buf(cp->c_hdl); 144*437Smws size_t bufoff = off - cp->c_eplen; 145*437Smws 146*437Smws err = uiomove(buf + bufoff, 147*437Smws MIN(len, cp->c_stlen - bufoff), UIO_READ, uio); 148*437Smws } 149*437Smws 150*437Smws return (err); 151*437Smws } 152*437Smws 153*437Smws /*ARGSUSED*/ 154*437Smws static int 155*437Smws smb_read(dev_t dev, uio_t *uio, cred_t *cred) 156*437Smws { 157*437Smws smb_clone_t *cp = &smb_clones[getminor(dev)]; 158*437Smws 159*437Smws if (uio->uio_offset < 0 || 160*437Smws uio->uio_offset >= cp->c_eplen + cp->c_stlen) 161*437Smws return (0); 162*437Smws 163*437Smws return (smb_uiomove(cp, uio)); 164*437Smws } 165*437Smws 166*437Smws /*ARGSUSED*/ 167*437Smws static int 168*437Smws smb_segmap(dev_t dev, off_t off, struct as *as, caddr_t *addrp, off_t len, 169*437Smws uint_t prot, uint_t maxprot, uint_t flags, cred_t *cred) 170*437Smws { 171*437Smws smb_clone_t *cp = &smb_clones[getminor(dev)]; 172*437Smws 173*437Smws size_t alen = P2ROUNDUP(len, PAGESIZE); 174*437Smws caddr_t addr; 175*437Smws 176*437Smws iovec_t iov; 177*437Smws uio_t uio; 178*437Smws int err; 179*437Smws 180*437Smws if (len <= 0 || (flags & MAP_FIXED)) 181*437Smws return (EINVAL); 182*437Smws 183*437Smws if ((prot & PROT_WRITE) && (flags & MAP_SHARED)) 184*437Smws return (EACCES); 185*437Smws 186*437Smws if (off < 0 || off + len < off || off + len > cp->c_eplen + cp->c_stlen) 187*437Smws return (ENXIO); 188*437Smws 189*437Smws as_rangelock(as); 190*437Smws map_addr(&addr, alen, 0, 1, 0); 191*437Smws 192*437Smws if (addr != NULL) 193*437Smws err = as_map(as, addr, alen, segvn_create, zfod_argsp); 194*437Smws else 195*437Smws err = ENOMEM; 196*437Smws 197*437Smws as_rangeunlock(as); 198*437Smws *addrp = addr; 199*437Smws 200*437Smws if (err != 0) 201*437Smws return (err); 202*437Smws 203*437Smws iov.iov_base = addr; 204*437Smws iov.iov_len = len; 205*437Smws 206*437Smws bzero(&uio, sizeof (uio_t)); 207*437Smws uio.uio_iov = &iov; 208*437Smws uio.uio_iovcnt = 1; 209*437Smws uio.uio_offset = off; 210*437Smws uio.uio_segflg = UIO_USERSPACE; 211*437Smws uio.uio_extflg = UIO_COPY_DEFAULT; 212*437Smws uio.uio_resid = len; 213*437Smws 214*437Smws if ((err = smb_uiomove(cp, &uio)) != 0) 215*437Smws (void) as_unmap(as, addr, alen); 216*437Smws 217*437Smws return (err); 218*437Smws } 219*437Smws 220*437Smws /*ARGSUSED*/ 221*437Smws static int 222*437Smws smb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 223*437Smws { 224*437Smws switch (infocmd) { 225*437Smws case DDI_INFO_DEVT2DEVINFO: 226*437Smws *result = smb_devi; 227*437Smws return (DDI_SUCCESS); 228*437Smws case DDI_INFO_DEVT2INSTANCE: 229*437Smws *result = 0; 230*437Smws return (DDI_SUCCESS); 231*437Smws } 232*437Smws return (DDI_FAILURE); 233*437Smws } 234*437Smws 235*437Smws static int 236*437Smws smb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 237*437Smws { 238*437Smws if (cmd != DDI_ATTACH) 239*437Smws return (DDI_FAILURE); 240*437Smws 241*437Smws if (ddi_create_minor_node(devi, "smbios", 242*437Smws S_IFCHR, 0, DDI_PSEUDO, 0) == DDI_FAILURE) { 243*437Smws ddi_remove_minor_node(devi, NULL); 244*437Smws return (DDI_FAILURE); 245*437Smws } 246*437Smws 247*437Smws smb_devi = devi; 248*437Smws return (DDI_SUCCESS); 249*437Smws } 250*437Smws 251*437Smws static int 252*437Smws smb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 253*437Smws { 254*437Smws if (cmd != DDI_DETACH) 255*437Smws return (DDI_FAILURE); 256*437Smws 257*437Smws ddi_remove_minor_node(devi, NULL); 258*437Smws return (DDI_SUCCESS); 259*437Smws } 260*437Smws 261*437Smws static struct cb_ops smb_cb_ops = { 262*437Smws smb_open, /* open */ 263*437Smws smb_close, /* close */ 264*437Smws nodev, /* strategy */ 265*437Smws nodev, /* print */ 266*437Smws nodev, /* dump */ 267*437Smws smb_read, /* read */ 268*437Smws nodev, /* write */ 269*437Smws nodev, /* ioctl */ 270*437Smws nodev, /* devmap */ 271*437Smws nodev, /* mmap */ 272*437Smws smb_segmap, /* segmap */ 273*437Smws nochpoll, /* poll */ 274*437Smws ddi_prop_op, /* prop_op */ 275*437Smws NULL, /* streamtab */ 276*437Smws D_NEW | D_MP /* flags */ 277*437Smws }; 278*437Smws 279*437Smws static struct dev_ops smb_ops = { 280*437Smws DEVO_REV, /* rev */ 281*437Smws 0, /* refcnt */ 282*437Smws smb_info, /* info */ 283*437Smws nulldev, /* identify */ 284*437Smws nulldev, /* probe */ 285*437Smws smb_attach, /* attach */ 286*437Smws smb_detach, /* detach */ 287*437Smws nodev, /* reset */ 288*437Smws &smb_cb_ops, /* cb ops */ 289*437Smws NULL /* bus ops */ 290*437Smws }; 291*437Smws 292*437Smws static struct modldrv modldrv = { 293*437Smws &mod_driverops, "System Management BIOS driver", &smb_ops, 294*437Smws }; 295*437Smws 296*437Smws static struct modlinkage modlinkage = { 297*437Smws MODREV_1, { (void *)&modldrv } 298*437Smws }; 299*437Smws 300*437Smws int 301*437Smws _init(void) 302*437Smws { 303*437Smws int err; 304*437Smws 305*437Smws if (smb_nclones <= 0) 306*437Smws smb_nclones = maxusers; 307*437Smws 308*437Smws smb_clones = kmem_zalloc(sizeof (smb_clone_t) * smb_nclones, KM_SLEEP); 309*437Smws 310*437Smws if ((err = mod_install(&modlinkage)) != 0) 311*437Smws kmem_free(smb_clones, sizeof (smb_clone_t) * smb_nclones); 312*437Smws 313*437Smws return (err); 314*437Smws } 315*437Smws 316*437Smws int 317*437Smws _fini(void) 318*437Smws { 319*437Smws int err; 320*437Smws 321*437Smws if ((err = mod_remove(&modlinkage)) == 0) 322*437Smws kmem_free(smb_clones, sizeof (smb_clone_t) * smb_nclones); 323*437Smws 324*437Smws return (err); 325*437Smws } 326*437Smws 327*437Smws int 328*437Smws _info(struct modinfo *mip) 329*437Smws { 330*437Smws return (mod_info(&modlinkage, mip)); 331*437Smws } 332