184ab085aSmws /* 284ab085aSmws * CDDL HEADER START 384ab085aSmws * 484ab085aSmws * The contents of this file are subject to the terms of the 560946fe0Smec * Common Development and Distribution License (the "License"). 660946fe0Smec * You may not use this file except in compliance with the License. 784ab085aSmws * 884ab085aSmws * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 984ab085aSmws * or http://www.opensolaris.org/os/licensing. 1084ab085aSmws * See the License for the specific language governing permissions 1184ab085aSmws * and limitations under the License. 1284ab085aSmws * 1384ab085aSmws * When distributing Covered Code, include this CDDL HEADER in each 1484ab085aSmws * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1584ab085aSmws * If applicable, add the following below this CDDL HEADER, with the 1684ab085aSmws * fields enclosed by brackets "[]" replaced with your own identifying 1784ab085aSmws * information: Portions Copyright [yyyy] [name of copyright owner] 1884ab085aSmws * 1984ab085aSmws * CDDL HEADER END 2084ab085aSmws */ 2184ab085aSmws 2284ab085aSmws /* 2360946fe0Smec * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 2484ab085aSmws * Use is subject to license terms. 2584ab085aSmws */ 2684ab085aSmws 2784ab085aSmws 2884ab085aSmws /* 2984ab085aSmws * smbios(7D) driver 3084ab085aSmws * 3184ab085aSmws * This pseudo-driver makes available a snapshot of the system's SMBIOS image 3284ab085aSmws * that can be accessed using libsmbios. Clients may access a snapshot using 3384ab085aSmws * either read(2) or mmap(2). The driver returns the SMBIOS entry point data 3484ab085aSmws * followed by the SMBIOS structure table. The entry point has its 'staddr' 3584ab085aSmws * field set to indicate the byte offset of the structure table. The driver 3684ab085aSmws * uses the common SMBIOS API defined in <sys/smbios.h> to access the image. 3784ab085aSmws * 3884ab085aSmws * At present, the kernel takes a single snapshot of SMBIOS at boot time and 3984ab085aSmws * stores a handle for this snapshot in 'ksmbios'. To keep track of driver 4084ab085aSmws * opens, we simply compare-and-swap this handle into an 'smb_clones' array. 4184ab085aSmws * Future x86 systems may need to support dynamic SMBIOS updates: when that 4284ab085aSmws * happens the SMBIOS API can be extended to support reference counting and 4384ab085aSmws * handles for different snapshots can be stored in smb_clones[]. 4484ab085aSmws */ 4584ab085aSmws 4684ab085aSmws #include <sys/smbios.h> 4784ab085aSmws #include <sys/sysmacros.h> 4884ab085aSmws #include <sys/cmn_err.h> 4984ab085aSmws #include <sys/vmsystm.h> 5084ab085aSmws #include <vm/seg_vn.h> 5184ab085aSmws #include <sys/ddi.h> 5284ab085aSmws #include <sys/sunddi.h> 5384ab085aSmws #include <sys/modctl.h> 5484ab085aSmws #include <sys/conf.h> 5584ab085aSmws #include <sys/stat.h> 5684ab085aSmws 5784ab085aSmws typedef struct smb_clone { 5884ab085aSmws smbios_hdl_t *c_hdl; 5984ab085aSmws size_t c_eplen; 6084ab085aSmws size_t c_stlen; 6184ab085aSmws } smb_clone_t; 6284ab085aSmws 6384ab085aSmws static dev_info_t *smb_devi; 6484ab085aSmws static smb_clone_t *smb_clones; 6584ab085aSmws static int smb_nclones; 6684ab085aSmws 6784ab085aSmws /*ARGSUSED*/ 6884ab085aSmws static int 6984ab085aSmws smb_open(dev_t *dp, int flag, int otyp, cred_t *cred) 7084ab085aSmws { 7184ab085aSmws minor_t c; 7284ab085aSmws 7384ab085aSmws if (ksmbios == NULL) 7484ab085aSmws return (ENXIO); 7584ab085aSmws 7684ab085aSmws /* 7784ab085aSmws * Locate and reserve a clone structure. We skip clone 0 as that is 7884ab085aSmws * the real minor number, and we assign a new minor to each clone. 7984ab085aSmws */ 8084ab085aSmws for (c = 1; c < smb_nclones; c++) { 8175d94465SJosef 'Jeff' Sipek if (atomic_cas_ptr(&smb_clones[c].c_hdl, NULL, ksmbios) == NULL) 8284ab085aSmws break; 8384ab085aSmws } 8484ab085aSmws 8584ab085aSmws if (c >= smb_nclones) 8684ab085aSmws return (EAGAIN); 8784ab085aSmws 8884ab085aSmws smb_clones[c].c_eplen = P2ROUNDUP(sizeof (smbios_entry_t), 16); 8984ab085aSmws smb_clones[c].c_stlen = smbios_buflen(smb_clones[c].c_hdl); 9084ab085aSmws 9184ab085aSmws *dp = makedevice(getemajor(*dp), c); 9284ab085aSmws 9384ab085aSmws (void) ddi_prop_update_int(*dp, smb_devi, "size", 9484ab085aSmws smb_clones[c].c_eplen + smb_clones[c].c_stlen); 9584ab085aSmws 9684ab085aSmws return (0); 9784ab085aSmws } 9884ab085aSmws 9984ab085aSmws /*ARGSUSED*/ 10084ab085aSmws static int 10184ab085aSmws smb_close(dev_t dev, int flag, int otyp, cred_t *cred) 10284ab085aSmws { 10384ab085aSmws (void) ddi_prop_remove(dev, smb_devi, "size"); 10484ab085aSmws smb_clones[getminor(dev)].c_hdl = NULL; 10584ab085aSmws return (0); 10684ab085aSmws } 10784ab085aSmws 10884ab085aSmws /* 10984ab085aSmws * Common code to copy out the SMBIOS snapshot used for both read and mmap. 11084ab085aSmws * The caller must validate uio_offset for us since semantics differ there. 11184ab085aSmws * The copy is done in two stages, either of which can be skipped based on the 11284ab085aSmws * offset and length: first we copy the entry point, with 'staddr' recalculated 11384ab085aSmws * to indicate the offset of the data buffer, and second we copy the table. 11484ab085aSmws */ 11584ab085aSmws static int 11684ab085aSmws smb_uiomove(smb_clone_t *cp, uio_t *uio) 11784ab085aSmws { 11884ab085aSmws off_t off = uio->uio_offset; 11984ab085aSmws size_t len = uio->uio_resid; 12084ab085aSmws int err = 0; 12184ab085aSmws 12284ab085aSmws if (off + len > cp->c_eplen + cp->c_stlen) 12384ab085aSmws len = cp->c_eplen + cp->c_stlen - off; 12484ab085aSmws 12584ab085aSmws if (off < cp->c_eplen) { 12684ab085aSmws smbios_entry_t *ep = kmem_zalloc(cp->c_eplen, KM_SLEEP); 12784ab085aSmws size_t eprlen = MIN(len, cp->c_eplen - off); 12884ab085aSmws 129*c325726fSToomas Soome switch (smbios_info_smbios(cp->c_hdl, ep)) { 130*c325726fSToomas Soome case SMBIOS_ENTRY_POINT_21: 131*c325726fSToomas Soome ep->ep21.smbe_staddr = (uint32_t)cp->c_eplen; 132*c325726fSToomas Soome break; 133*c325726fSToomas Soome case SMBIOS_ENTRY_POINT_30: 134*c325726fSToomas Soome ep->ep30.smbe_staddr = (uint64_t)cp->c_eplen; 135*c325726fSToomas Soome break; 136*c325726fSToomas Soome } 13784ab085aSmws smbios_checksum(cp->c_hdl, ep); 13884ab085aSmws 13984ab085aSmws err = uiomove((char *)ep + off, eprlen, UIO_READ, uio); 14084ab085aSmws kmem_free(ep, cp->c_eplen); 14184ab085aSmws 14284ab085aSmws off += eprlen; 14384ab085aSmws len -= eprlen; 14484ab085aSmws } 14584ab085aSmws 14684ab085aSmws if (err == 0 && off >= cp->c_eplen) { 14784ab085aSmws char *buf = (char *)smbios_buf(cp->c_hdl); 14884ab085aSmws size_t bufoff = off - cp->c_eplen; 14984ab085aSmws 15084ab085aSmws err = uiomove(buf + bufoff, 15184ab085aSmws MIN(len, cp->c_stlen - bufoff), UIO_READ, uio); 15284ab085aSmws } 15384ab085aSmws 15484ab085aSmws return (err); 15584ab085aSmws } 15684ab085aSmws 15784ab085aSmws /*ARGSUSED*/ 15884ab085aSmws static int 15984ab085aSmws smb_read(dev_t dev, uio_t *uio, cred_t *cred) 16084ab085aSmws { 16184ab085aSmws smb_clone_t *cp = &smb_clones[getminor(dev)]; 16284ab085aSmws 16384ab085aSmws if (uio->uio_offset < 0 || 16484ab085aSmws uio->uio_offset >= cp->c_eplen + cp->c_stlen) 16584ab085aSmws return (0); 16684ab085aSmws 16784ab085aSmws return (smb_uiomove(cp, uio)); 16884ab085aSmws } 16984ab085aSmws 17084ab085aSmws /*ARGSUSED*/ 17184ab085aSmws static int 17284ab085aSmws smb_segmap(dev_t dev, off_t off, struct as *as, caddr_t *addrp, off_t len, 17384ab085aSmws uint_t prot, uint_t maxprot, uint_t flags, cred_t *cred) 17484ab085aSmws { 17584ab085aSmws smb_clone_t *cp = &smb_clones[getminor(dev)]; 17684ab085aSmws 17784ab085aSmws size_t alen = P2ROUNDUP(len, PAGESIZE); 17860946fe0Smec caddr_t addr = NULL; 17984ab085aSmws 18084ab085aSmws iovec_t iov; 18184ab085aSmws uio_t uio; 18284ab085aSmws int err; 18384ab085aSmws 18484ab085aSmws if (len <= 0 || (flags & MAP_FIXED)) 18584ab085aSmws return (EINVAL); 18684ab085aSmws 18784ab085aSmws if ((prot & PROT_WRITE) && (flags & MAP_SHARED)) 18884ab085aSmws return (EACCES); 18984ab085aSmws 19084ab085aSmws if (off < 0 || off + len < off || off + len > cp->c_eplen + cp->c_stlen) 19184ab085aSmws return (ENXIO); 19284ab085aSmws 19384ab085aSmws as_rangelock(as); 19484ab085aSmws map_addr(&addr, alen, 0, 1, 0); 19584ab085aSmws 19684ab085aSmws if (addr != NULL) 19784ab085aSmws err = as_map(as, addr, alen, segvn_create, zfod_argsp); 19884ab085aSmws else 19984ab085aSmws err = ENOMEM; 20084ab085aSmws 20184ab085aSmws as_rangeunlock(as); 20284ab085aSmws *addrp = addr; 20384ab085aSmws 20484ab085aSmws if (err != 0) 20584ab085aSmws return (err); 20684ab085aSmws 20784ab085aSmws iov.iov_base = addr; 20884ab085aSmws iov.iov_len = len; 20984ab085aSmws 21084ab085aSmws bzero(&uio, sizeof (uio_t)); 21184ab085aSmws uio.uio_iov = &iov; 21284ab085aSmws uio.uio_iovcnt = 1; 21384ab085aSmws uio.uio_offset = off; 21484ab085aSmws uio.uio_segflg = UIO_USERSPACE; 21584ab085aSmws uio.uio_extflg = UIO_COPY_DEFAULT; 21684ab085aSmws uio.uio_resid = len; 21784ab085aSmws 21884ab085aSmws if ((err = smb_uiomove(cp, &uio)) != 0) 21984ab085aSmws (void) as_unmap(as, addr, alen); 22084ab085aSmws 22184ab085aSmws return (err); 22284ab085aSmws } 22384ab085aSmws 22484ab085aSmws /*ARGSUSED*/ 22584ab085aSmws static int 22684ab085aSmws smb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 22784ab085aSmws { 22884ab085aSmws switch (infocmd) { 22984ab085aSmws case DDI_INFO_DEVT2DEVINFO: 23084ab085aSmws *result = smb_devi; 23184ab085aSmws return (DDI_SUCCESS); 23284ab085aSmws case DDI_INFO_DEVT2INSTANCE: 23384ab085aSmws *result = 0; 23484ab085aSmws return (DDI_SUCCESS); 23584ab085aSmws } 23684ab085aSmws return (DDI_FAILURE); 23784ab085aSmws } 23884ab085aSmws 23984ab085aSmws static int 24084ab085aSmws smb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 24184ab085aSmws { 24284ab085aSmws if (cmd != DDI_ATTACH) 24384ab085aSmws return (DDI_FAILURE); 24484ab085aSmws 24584ab085aSmws if (ddi_create_minor_node(devi, "smbios", 24684ab085aSmws S_IFCHR, 0, DDI_PSEUDO, 0) == DDI_FAILURE) { 24784ab085aSmws ddi_remove_minor_node(devi, NULL); 24884ab085aSmws return (DDI_FAILURE); 24984ab085aSmws } 25084ab085aSmws 25184ab085aSmws smb_devi = devi; 25284ab085aSmws return (DDI_SUCCESS); 25384ab085aSmws } 25484ab085aSmws 25584ab085aSmws static int 25684ab085aSmws smb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 25784ab085aSmws { 25884ab085aSmws if (cmd != DDI_DETACH) 25984ab085aSmws return (DDI_FAILURE); 26084ab085aSmws 26184ab085aSmws ddi_remove_minor_node(devi, NULL); 26284ab085aSmws return (DDI_SUCCESS); 26384ab085aSmws } 26484ab085aSmws 26584ab085aSmws static struct cb_ops smb_cb_ops = { 26684ab085aSmws smb_open, /* open */ 26784ab085aSmws smb_close, /* close */ 26884ab085aSmws nodev, /* strategy */ 26984ab085aSmws nodev, /* print */ 27084ab085aSmws nodev, /* dump */ 27184ab085aSmws smb_read, /* read */ 27284ab085aSmws nodev, /* write */ 27384ab085aSmws nodev, /* ioctl */ 27484ab085aSmws nodev, /* devmap */ 27584ab085aSmws nodev, /* mmap */ 27684ab085aSmws smb_segmap, /* segmap */ 27784ab085aSmws nochpoll, /* poll */ 27884ab085aSmws ddi_prop_op, /* prop_op */ 27984ab085aSmws NULL, /* streamtab */ 28084ab085aSmws D_NEW | D_MP /* flags */ 28184ab085aSmws }; 28284ab085aSmws 28384ab085aSmws static struct dev_ops smb_ops = { 28484ab085aSmws DEVO_REV, /* rev */ 28584ab085aSmws 0, /* refcnt */ 28684ab085aSmws smb_info, /* info */ 28784ab085aSmws nulldev, /* identify */ 28884ab085aSmws nulldev, /* probe */ 28984ab085aSmws smb_attach, /* attach */ 29084ab085aSmws smb_detach, /* detach */ 29184ab085aSmws nodev, /* reset */ 29284ab085aSmws &smb_cb_ops, /* cb ops */ 29319397407SSherry Moore NULL, /* bus ops */ 29419397407SSherry Moore NULL, /* power */ 29519397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 29684ab085aSmws }; 29784ab085aSmws 29884ab085aSmws static struct modldrv modldrv = { 29984ab085aSmws &mod_driverops, "System Management BIOS driver", &smb_ops, 30084ab085aSmws }; 30184ab085aSmws 30284ab085aSmws static struct modlinkage modlinkage = { 30384ab085aSmws MODREV_1, { (void *)&modldrv } 30484ab085aSmws }; 30584ab085aSmws 30684ab085aSmws int 30784ab085aSmws _init(void) 30884ab085aSmws { 30984ab085aSmws int err; 31084ab085aSmws 31184ab085aSmws if (smb_nclones <= 0) 31284ab085aSmws smb_nclones = maxusers; 31384ab085aSmws 31484ab085aSmws smb_clones = kmem_zalloc(sizeof (smb_clone_t) * smb_nclones, KM_SLEEP); 31584ab085aSmws 31684ab085aSmws if ((err = mod_install(&modlinkage)) != 0) 31784ab085aSmws kmem_free(smb_clones, sizeof (smb_clone_t) * smb_nclones); 31884ab085aSmws 31984ab085aSmws return (err); 32084ab085aSmws } 32184ab085aSmws 32284ab085aSmws int 32384ab085aSmws _fini(void) 32484ab085aSmws { 32584ab085aSmws int err; 32684ab085aSmws 32784ab085aSmws if ((err = mod_remove(&modlinkage)) == 0) 32884ab085aSmws kmem_free(smb_clones, sizeof (smb_clone_t) * smb_nclones); 32984ab085aSmws 33084ab085aSmws return (err); 33184ab085aSmws } 33284ab085aSmws 33384ab085aSmws int 33484ab085aSmws _info(struct modinfo *mip) 33584ab085aSmws { 33684ab085aSmws return (mod_info(&modlinkage, mip)); 33784ab085aSmws } 338