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