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