xref: /onnv-gate/usr/src/uts/common/io/smbios.c (revision 437:76c202dd62bf)
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