xref: /titanic_52/usr/src/uts/common/io/smbios.c (revision 84ab085a13f931bc78e7415e7ce921dbaa14fcb3)
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