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