1437Smws /* 2437Smws * CDDL HEADER START 3437Smws * 4437Smws * The contents of this file are subject to the terms of the 5*6036Smec * Common Development and Distribution License (the "License"). 6*6036Smec * You may not use this file except in compliance with the License. 7437Smws * 8437Smws * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9437Smws * or http://www.opensolaris.org/os/licensing. 10437Smws * See the License for the specific language governing permissions 11437Smws * and limitations under the License. 12437Smws * 13437Smws * When distributing Covered Code, include this CDDL HEADER in each 14437Smws * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15437Smws * If applicable, add the following below this CDDL HEADER, with the 16437Smws * fields enclosed by brackets "[]" replaced with your own identifying 17437Smws * information: Portions Copyright [yyyy] [name of copyright owner] 18437Smws * 19437Smws * CDDL HEADER END 20437Smws */ 21437Smws 22437Smws /* 23*6036Smec * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24437Smws * Use is subject to license terms. 25437Smws */ 26437Smws 27437Smws #pragma ident "%Z%%M% %I% %E% SMI" 28437Smws 29437Smws /* 30437Smws * smbios(7D) driver 31437Smws * 32437Smws * This pseudo-driver makes available a snapshot of the system's SMBIOS image 33437Smws * that can be accessed using libsmbios. Clients may access a snapshot using 34437Smws * either read(2) or mmap(2). The driver returns the SMBIOS entry point data 35437Smws * followed by the SMBIOS structure table. The entry point has its 'staddr' 36437Smws * field set to indicate the byte offset of the structure table. The driver 37437Smws * uses the common SMBIOS API defined in <sys/smbios.h> to access the image. 38437Smws * 39437Smws * At present, the kernel takes a single snapshot of SMBIOS at boot time and 40437Smws * stores a handle for this snapshot in 'ksmbios'. To keep track of driver 41437Smws * opens, we simply compare-and-swap this handle into an 'smb_clones' array. 42437Smws * Future x86 systems may need to support dynamic SMBIOS updates: when that 43437Smws * happens the SMBIOS API can be extended to support reference counting and 44437Smws * handles for different snapshots can be stored in smb_clones[]. 45437Smws */ 46437Smws 47437Smws #include <sys/smbios.h> 48437Smws #include <sys/sysmacros.h> 49437Smws #include <sys/cmn_err.h> 50437Smws #include <sys/vmsystm.h> 51437Smws #include <vm/seg_vn.h> 52437Smws #include <sys/ddi.h> 53437Smws #include <sys/sunddi.h> 54437Smws #include <sys/modctl.h> 55437Smws #include <sys/conf.h> 56437Smws #include <sys/stat.h> 57437Smws 58437Smws typedef struct smb_clone { 59437Smws smbios_hdl_t *c_hdl; 60437Smws size_t c_eplen; 61437Smws size_t c_stlen; 62437Smws } smb_clone_t; 63437Smws 64437Smws static dev_info_t *smb_devi; 65437Smws static smb_clone_t *smb_clones; 66437Smws static int smb_nclones; 67437Smws 68437Smws /*ARGSUSED*/ 69437Smws static int 70437Smws smb_open(dev_t *dp, int flag, int otyp, cred_t *cred) 71437Smws { 72437Smws minor_t c; 73437Smws 74437Smws if (ksmbios == NULL) 75437Smws return (ENXIO); 76437Smws 77437Smws /* 78437Smws * Locate and reserve a clone structure. We skip clone 0 as that is 79437Smws * the real minor number, and we assign a new minor to each clone. 80437Smws */ 81437Smws for (c = 1; c < smb_nclones; c++) { 82437Smws if (casptr(&smb_clones[c].c_hdl, NULL, ksmbios) == NULL) 83437Smws break; 84437Smws } 85437Smws 86437Smws if (c >= smb_nclones) 87437Smws return (EAGAIN); 88437Smws 89437Smws smb_clones[c].c_eplen = P2ROUNDUP(sizeof (smbios_entry_t), 16); 90437Smws smb_clones[c].c_stlen = smbios_buflen(smb_clones[c].c_hdl); 91437Smws 92437Smws *dp = makedevice(getemajor(*dp), c); 93437Smws 94437Smws (void) ddi_prop_update_int(*dp, smb_devi, "size", 95437Smws smb_clones[c].c_eplen + smb_clones[c].c_stlen); 96437Smws 97437Smws return (0); 98437Smws } 99437Smws 100437Smws /*ARGSUSED*/ 101437Smws static int 102437Smws smb_close(dev_t dev, int flag, int otyp, cred_t *cred) 103437Smws { 104437Smws (void) ddi_prop_remove(dev, smb_devi, "size"); 105437Smws smb_clones[getminor(dev)].c_hdl = NULL; 106437Smws return (0); 107437Smws } 108437Smws 109437Smws /* 110437Smws * Common code to copy out the SMBIOS snapshot used for both read and mmap. 111437Smws * The caller must validate uio_offset for us since semantics differ there. 112437Smws * The copy is done in two stages, either of which can be skipped based on the 113437Smws * offset and length: first we copy the entry point, with 'staddr' recalculated 114437Smws * to indicate the offset of the data buffer, and second we copy the table. 115437Smws */ 116437Smws static int 117437Smws smb_uiomove(smb_clone_t *cp, uio_t *uio) 118437Smws { 119437Smws off_t off = uio->uio_offset; 120437Smws size_t len = uio->uio_resid; 121437Smws int err = 0; 122437Smws 123437Smws if (off + len > cp->c_eplen + cp->c_stlen) 124437Smws len = cp->c_eplen + cp->c_stlen - off; 125437Smws 126437Smws if (off < cp->c_eplen) { 127437Smws smbios_entry_t *ep = kmem_zalloc(cp->c_eplen, KM_SLEEP); 128437Smws size_t eprlen = MIN(len, cp->c_eplen - off); 129437Smws 130437Smws smbios_info_smbios(cp->c_hdl, ep); 131437Smws ep->smbe_staddr = (uint32_t)cp->c_eplen; 132437Smws smbios_checksum(cp->c_hdl, ep); 133437Smws 134437Smws err = uiomove((char *)ep + off, eprlen, UIO_READ, uio); 135437Smws kmem_free(ep, cp->c_eplen); 136437Smws 137437Smws off += eprlen; 138437Smws len -= eprlen; 139437Smws } 140437Smws 141437Smws if (err == 0 && off >= cp->c_eplen) { 142437Smws char *buf = (char *)smbios_buf(cp->c_hdl); 143437Smws size_t bufoff = off - cp->c_eplen; 144437Smws 145437Smws err = uiomove(buf + bufoff, 146437Smws MIN(len, cp->c_stlen - bufoff), UIO_READ, uio); 147437Smws } 148437Smws 149437Smws return (err); 150437Smws } 151437Smws 152437Smws /*ARGSUSED*/ 153437Smws static int 154437Smws smb_read(dev_t dev, uio_t *uio, cred_t *cred) 155437Smws { 156437Smws smb_clone_t *cp = &smb_clones[getminor(dev)]; 157437Smws 158437Smws if (uio->uio_offset < 0 || 159437Smws uio->uio_offset >= cp->c_eplen + cp->c_stlen) 160437Smws return (0); 161437Smws 162437Smws return (smb_uiomove(cp, uio)); 163437Smws } 164437Smws 165437Smws /*ARGSUSED*/ 166437Smws static int 167437Smws smb_segmap(dev_t dev, off_t off, struct as *as, caddr_t *addrp, off_t len, 168437Smws uint_t prot, uint_t maxprot, uint_t flags, cred_t *cred) 169437Smws { 170437Smws smb_clone_t *cp = &smb_clones[getminor(dev)]; 171437Smws 172437Smws size_t alen = P2ROUNDUP(len, PAGESIZE); 173*6036Smec caddr_t addr = NULL; 174437Smws 175437Smws iovec_t iov; 176437Smws uio_t uio; 177437Smws int err; 178437Smws 179437Smws if (len <= 0 || (flags & MAP_FIXED)) 180437Smws return (EINVAL); 181437Smws 182437Smws if ((prot & PROT_WRITE) && (flags & MAP_SHARED)) 183437Smws return (EACCES); 184437Smws 185437Smws if (off < 0 || off + len < off || off + len > cp->c_eplen + cp->c_stlen) 186437Smws return (ENXIO); 187437Smws 188437Smws as_rangelock(as); 189437Smws map_addr(&addr, alen, 0, 1, 0); 190437Smws 191437Smws if (addr != NULL) 192437Smws err = as_map(as, addr, alen, segvn_create, zfod_argsp); 193437Smws else 194437Smws err = ENOMEM; 195437Smws 196437Smws as_rangeunlock(as); 197437Smws *addrp = addr; 198437Smws 199437Smws if (err != 0) 200437Smws return (err); 201437Smws 202437Smws iov.iov_base = addr; 203437Smws iov.iov_len = len; 204437Smws 205437Smws bzero(&uio, sizeof (uio_t)); 206437Smws uio.uio_iov = &iov; 207437Smws uio.uio_iovcnt = 1; 208437Smws uio.uio_offset = off; 209437Smws uio.uio_segflg = UIO_USERSPACE; 210437Smws uio.uio_extflg = UIO_COPY_DEFAULT; 211437Smws uio.uio_resid = len; 212437Smws 213437Smws if ((err = smb_uiomove(cp, &uio)) != 0) 214437Smws (void) as_unmap(as, addr, alen); 215437Smws 216437Smws return (err); 217437Smws } 218437Smws 219437Smws /*ARGSUSED*/ 220437Smws static int 221437Smws smb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 222437Smws { 223437Smws switch (infocmd) { 224437Smws case DDI_INFO_DEVT2DEVINFO: 225437Smws *result = smb_devi; 226437Smws return (DDI_SUCCESS); 227437Smws case DDI_INFO_DEVT2INSTANCE: 228437Smws *result = 0; 229437Smws return (DDI_SUCCESS); 230437Smws } 231437Smws return (DDI_FAILURE); 232437Smws } 233437Smws 234437Smws static int 235437Smws smb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 236437Smws { 237437Smws if (cmd != DDI_ATTACH) 238437Smws return (DDI_FAILURE); 239437Smws 240437Smws if (ddi_create_minor_node(devi, "smbios", 241437Smws S_IFCHR, 0, DDI_PSEUDO, 0) == DDI_FAILURE) { 242437Smws ddi_remove_minor_node(devi, NULL); 243437Smws return (DDI_FAILURE); 244437Smws } 245437Smws 246437Smws smb_devi = devi; 247437Smws return (DDI_SUCCESS); 248437Smws } 249437Smws 250437Smws static int 251437Smws smb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 252437Smws { 253437Smws if (cmd != DDI_DETACH) 254437Smws return (DDI_FAILURE); 255437Smws 256437Smws ddi_remove_minor_node(devi, NULL); 257437Smws return (DDI_SUCCESS); 258437Smws } 259437Smws 260437Smws static struct cb_ops smb_cb_ops = { 261437Smws smb_open, /* open */ 262437Smws smb_close, /* close */ 263437Smws nodev, /* strategy */ 264437Smws nodev, /* print */ 265437Smws nodev, /* dump */ 266437Smws smb_read, /* read */ 267437Smws nodev, /* write */ 268437Smws nodev, /* ioctl */ 269437Smws nodev, /* devmap */ 270437Smws nodev, /* mmap */ 271437Smws smb_segmap, /* segmap */ 272437Smws nochpoll, /* poll */ 273437Smws ddi_prop_op, /* prop_op */ 274437Smws NULL, /* streamtab */ 275437Smws D_NEW | D_MP /* flags */ 276437Smws }; 277437Smws 278437Smws static struct dev_ops smb_ops = { 279437Smws DEVO_REV, /* rev */ 280437Smws 0, /* refcnt */ 281437Smws smb_info, /* info */ 282437Smws nulldev, /* identify */ 283437Smws nulldev, /* probe */ 284437Smws smb_attach, /* attach */ 285437Smws smb_detach, /* detach */ 286437Smws nodev, /* reset */ 287437Smws &smb_cb_ops, /* cb ops */ 288437Smws NULL /* bus ops */ 289437Smws }; 290437Smws 291437Smws static struct modldrv modldrv = { 292437Smws &mod_driverops, "System Management BIOS driver", &smb_ops, 293437Smws }; 294437Smws 295437Smws static struct modlinkage modlinkage = { 296437Smws MODREV_1, { (void *)&modldrv } 297437Smws }; 298437Smws 299437Smws int 300437Smws _init(void) 301437Smws { 302437Smws int err; 303437Smws 304437Smws if (smb_nclones <= 0) 305437Smws smb_nclones = maxusers; 306437Smws 307437Smws smb_clones = kmem_zalloc(sizeof (smb_clone_t) * smb_nclones, KM_SLEEP); 308437Smws 309437Smws if ((err = mod_install(&modlinkage)) != 0) 310437Smws kmem_free(smb_clones, sizeof (smb_clone_t) * smb_nclones); 311437Smws 312437Smws return (err); 313437Smws } 314437Smws 315437Smws int 316437Smws _fini(void) 317437Smws { 318437Smws int err; 319437Smws 320437Smws if ((err = mod_remove(&modlinkage)) == 0) 321437Smws kmem_free(smb_clones, sizeof (smb_clone_t) * smb_nclones); 322437Smws 323437Smws return (err); 324437Smws } 325437Smws 326437Smws int 327437Smws _info(struct modinfo *mip) 328437Smws { 329437Smws return (mod_info(&modlinkage, mip)); 330437Smws } 331