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