4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
31 * This pseudo-driver makes available a snapshot of the system's SMBIOS image
32 * that can be accessed using libsmbios. Clients may access a snapshot using
33 * either read(2) or mmap(2). The driver returns the SMBIOS entry point data
34 * followed by the SMBIOS structure table. The entry point has its 'staddr'
35 * field set to indicate the byte offset of the structure table. The driver
36 * uses the common SMBIOS API defined in <sys/smbios.h> to access the image.
38 * At present, the kernel takes a single snapshot of SMBIOS at boot time and
39 * stores a handle for this snapshot in 'ksmbios'. To keep track of driver
40 * opens, we simply compare-and-swap this handle into an 'smb_clones' array.
41 * Future x86 systems may need to support dynamic SMBIOS updates: when that
42 * happens the SMBIOS API can be extended to support reference counting and
43 * handles for different snapshots can be stored in smb_clones[].
46 #include <sys/smbios.h>
47 #include <sys/sysmacros.h>
48 #include <sys/cmn_err.h>
49 #include <sys/vmsystm.h>
50 #include <vm/seg_vn.h>
52 #include <sys/sunddi.h>
53 #include <sys/modctl.h>
57 typedef struct smb_clone
{
63 static dev_info_t
*smb_devi
;
64 static smb_clone_t
*smb_clones
;
65 static int smb_nclones
;
69 smb_open(dev_t
*dp
, int flag
, int otyp
, cred_t
*cred
)
77 * Locate and reserve a clone structure. We skip clone 0 as that is
78 * the real minor number, and we assign a new minor to each clone.
80 for (c
= 1; c
< smb_nclones
; c
++) {
81 if (atomic_cas_ptr(&smb_clones
[c
].c_hdl
, NULL
, ksmbios
) == NULL
)
88 smb_clones
[c
].c_eplen
= P2ROUNDUP(sizeof (smbios_entry_t
), 16);
89 smb_clones
[c
].c_stlen
= smbios_buflen(smb_clones
[c
].c_hdl
);
91 *dp
= makedevice(getemajor(*dp
), c
);
93 (void) ddi_prop_update_int(*dp
, smb_devi
, "size",
94 smb_clones
[c
].c_eplen
+ smb_clones
[c
].c_stlen
);
101 smb_close(dev_t dev
, int flag
, int otyp
, cred_t
*cred
)
103 (void) ddi_prop_remove(dev
, smb_devi
, "size");
104 smb_clones
[getminor(dev
)].c_hdl
= NULL
;
109 * Common code to copy out the SMBIOS snapshot used for both read and mmap.
110 * The caller must validate uio_offset for us since semantics differ there.
111 * The copy is done in two stages, either of which can be skipped based on the
112 * offset and length: first we copy the entry point, with 'staddr' recalculated
113 * to indicate the offset of the data buffer, and second we copy the table.
116 smb_uiomove(smb_clone_t
*cp
, uio_t
*uio
)
118 off_t off
= uio
->uio_offset
;
119 size_t len
= uio
->uio_resid
;
122 if (off
+ len
> cp
->c_eplen
+ cp
->c_stlen
)
123 len
= cp
->c_eplen
+ cp
->c_stlen
- off
;
125 if (off
< cp
->c_eplen
) {
126 smbios_entry_t
*ep
= kmem_zalloc(cp
->c_eplen
, KM_SLEEP
);
127 size_t eprlen
= MIN(len
, cp
->c_eplen
- off
);
129 switch (smbios_info_smbios(cp
->c_hdl
, ep
)) {
130 case SMBIOS_ENTRY_POINT_21
:
131 ep
->ep21
.smbe_staddr
= (uint32_t)cp
->c_eplen
;
133 case SMBIOS_ENTRY_POINT_30
:
134 ep
->ep30
.smbe_staddr
= (uint64_t)cp
->c_eplen
;
137 smbios_checksum(cp
->c_hdl
, ep
);
139 err
= uiomove((char *)ep
+ off
, eprlen
, UIO_READ
, uio
);
140 kmem_free(ep
, cp
->c_eplen
);
146 if (err
== 0 && off
>= cp
->c_eplen
) {
147 char *buf
= (char *)smbios_buf(cp
->c_hdl
);
148 size_t bufoff
= off
- cp
->c_eplen
;
150 err
= uiomove(buf
+ bufoff
,
151 MIN(len
, cp
->c_stlen
- bufoff
), UIO_READ
, uio
);
159 smb_read(dev_t dev
, uio_t
*uio
, cred_t
*cred
)
161 smb_clone_t
*cp
= &smb_clones
[getminor(dev
)];
163 if (uio
->uio_offset
< 0 ||
164 uio
->uio_offset
>= cp
->c_eplen
+ cp
->c_stlen
)
167 return (smb_uiomove(cp
, uio
));
172 smb_segmap(dev_t dev
, off_t off
, struct as
*as
, caddr_t
*addrp
, off_t len
,
173 uint_t prot
, uint_t maxprot
, uint_t flags
, cred_t
*cred
)
175 smb_clone_t
*cp
= &smb_clones
[getminor(dev
)];
177 size_t alen
= P2ROUNDUP(len
, PAGESIZE
);
184 if (len
<= 0 || (flags
& MAP_FIXED
))
187 if ((prot
& PROT_WRITE
) && (flags
& MAP_SHARED
))
190 if (off
< 0 || off
+ len
< off
|| off
+ len
> cp
->c_eplen
+ cp
->c_stlen
)
194 map_addr(&addr
, alen
, 0, 1, 0);
197 err
= as_map(as
, addr
, alen
, segvn_create
, zfod_argsp
);
210 bzero(&uio
, sizeof (uio_t
));
213 uio
.uio_offset
= off
;
214 uio
.uio_segflg
= UIO_USERSPACE
;
215 uio
.uio_extflg
= UIO_COPY_DEFAULT
;
218 if ((err
= smb_uiomove(cp
, &uio
)) != 0)
219 (void) as_unmap(as
, addr
, alen
);
226 smb_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
229 case DDI_INFO_DEVT2DEVINFO
:
231 return (DDI_SUCCESS
);
232 case DDI_INFO_DEVT2INSTANCE
:
234 return (DDI_SUCCESS
);
236 return (DDI_FAILURE
);
240 smb_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
242 if (cmd
!= DDI_ATTACH
)
243 return (DDI_FAILURE
);
245 if (ddi_create_minor_node(devi
, "smbios",
246 S_IFCHR
, 0, DDI_PSEUDO
, 0) == DDI_FAILURE
) {
247 ddi_remove_minor_node(devi
, NULL
);
248 return (DDI_FAILURE
);
252 return (DDI_SUCCESS
);
256 smb_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
258 if (cmd
!= DDI_DETACH
)
259 return (DDI_FAILURE
);
261 ddi_remove_minor_node(devi
, NULL
);
262 return (DDI_SUCCESS
);
265 static struct cb_ops smb_cb_ops
= {
267 smb_close
, /* close */
268 nodev
, /* strategy */
276 smb_segmap
, /* segmap */
278 ddi_prop_op
, /* prop_op */
279 NULL
, /* streamtab */
280 D_NEW
| D_MP
/* flags */
283 static struct dev_ops smb_ops
= {
287 nulldev
, /* identify */
289 smb_attach
, /* attach */
290 smb_detach
, /* detach */
292 &smb_cb_ops
, /* cb ops */
295 ddi_quiesce_not_needed
, /* quiesce */
298 static struct modldrv modldrv
= {
299 &mod_driverops
, "System Management BIOS driver", &smb_ops
,
302 static struct modlinkage modlinkage
= {
303 MODREV_1
, { (void *)&modldrv
}
311 if (smb_nclones
<= 0)
312 smb_nclones
= maxusers
;
314 smb_clones
= kmem_zalloc(sizeof (smb_clone_t
) * smb_nclones
, KM_SLEEP
);
316 if ((err
= mod_install(&modlinkage
)) != 0)
317 kmem_free(smb_clones
, sizeof (smb_clone_t
) * smb_nclones
);
327 if ((err
= mod_remove(&modlinkage
)) == 0)
328 kmem_free(smb_clones
, sizeof (smb_clone_t
) * smb_nclones
);
334 _info(struct modinfo
*mip
)
336 return (mod_info(&modlinkage
, mip
));