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 2015 OmniTI Computer Consulting, Inc. All rights reserved.
24 * Copyright 2016 Joyent, Inc.
25 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
29 #include <sys/smbios_impl.h>
31 static const uint_t _smb_hashlen
= 64; /* hash length (must be Pof2) */
32 static const char _smb_emptystr
[] = ""; /* empty string to return */
33 int _smb_debug
= 0; /* default debug mode */
36 * Strip out identification information for you privacy weenies. This is quite
37 * simple using our smbios_info_common() abstraction: we just locate any serial
38 * numbers and asset tags for each record, and then zero out those strings.
39 * Then we must handle two special cases: SMB_TYPE_SYSTEM holds a 16-byte UUID
40 * and SMB_TYPE_BATTERY stores a Smart Battery Data Spec 16-bit serial number.
41 * We use a literal '0' rather than '\0' for zeroing strings because \0\0 in
42 * the SMBIOS string table has a special meaning (denotes end-of-record).
45 smb_strip(smbios_hdl_t
*shp
)
49 for (i
= 0; i
< shp
->sh_nstructs
; i
++) {
50 const smb_header_t
*hp
= shp
->sh_structs
[i
].smbst_hdr
;
54 if (hp
->smbh_type
== SMB_TYPE_SYSTEM
&&
55 hp
->smbh_len
>= offsetof(smb_system_t
, smbsi_wakeup
)) {
56 smb_system_t
*sp
= (smb_system_t
*)(uintptr_t)hp
;
57 bzero(sp
->smbsi_uuid
, sizeof (sp
->smbsi_uuid
));
60 if (hp
->smbh_type
== SMB_TYPE_BATTERY
&&
61 hp
->smbh_len
>= offsetof(smb_battery_t
, smbbat_sdate
)) {
62 smb_battery_t
*bp
= (smb_battery_t
*)(uintptr_t)hp
;
66 if (smbios_info_common(shp
, hp
->smbh_hdl
, &info
) != SMB_ERR
) {
67 for (p
= (char *)info
.smbi_serial
; *p
!= '\0'; p
++)
69 for (p
= (char *)info
.smbi_asset
; *p
!= '\0'; p
++)
76 smbios_bufopen_21(smbios_hdl_t
*shp
, const smbios_21_entry_t
*ep
, size_t len
,
79 if (strncmp(ep
->smbe_eanchor
, SMB_ENTRY_EANCHOR
, SMB_ENTRY_EANCHORLEN
))
82 if (strncmp(ep
->smbe_ianchor
, SMB_ENTRY_IANCHOR
, SMB_ENTRY_IANCHORLEN
))
85 smb_dprintf(shp
, "opening SMBIOS version %u.%u bcdrev 0x%x\n",
86 ep
->smbe_major
, ep
->smbe_minor
, ep
->smbe_bcdrev
);
88 if (!(flags
& SMB_O_NOVERS
)) {
89 if (ep
->smbe_major
> SMB_MAJOR(SMB_VERSION
))
92 if (ep
->smbe_major
< SMB_MAJOR(SMB_VERSION_23
) || (
93 ep
->smbe_major
== SMB_MAJOR(SMB_VERSION_23
) &&
94 ep
->smbe_minor
< SMB_MINOR(SMB_VERSION_23
)))
98 if (len
< sizeof (smb_header_t
) ||
99 ep
->smbe_stlen
< sizeof (smb_header_t
) || len
< ep
->smbe_stlen
)
102 if (!(flags
& SMB_O_NOCKSUM
)) {
103 uint8_t esum
= 0, isum
= 0;
104 const uchar_t
*p
, *q
;
107 for (p
= q
; p
< q
+ ep
->smbe_elen
; p
++)
110 for (p
= (uchar_t
*)ep
->smbe_ianchor
; p
< q
+ sizeof (*ep
); p
++)
113 if (esum
!= 0 || isum
!= 0) {
114 smb_dprintf(shp
, "bad cksum: e=%x i=%x\n", esum
, isum
);
120 * Copy the entry point into our handle. The underlying entry point
121 * may be larger than our structure definition, so reset smbe_elen
122 * to our internal size and recompute good checksums for our copy.
124 shp
->sh_ent_type
= SMBIOS_ENTRY_POINT_21
;
125 bcopy(ep
, &shp
->sh_ent
, sizeof (smbios_entry_t
));
126 shp
->sh_ent
.ep21
.smbe_elen
= sizeof (smbios_entry_t
);
127 smbios_checksum(shp
, &shp
->sh_ent
);
129 shp
->sh_ent_stnum
= ep
->smbe_stnum
;
130 shp
->sh_smbvers
= SMB_MAJMIN(ep
->smbe_major
, ep
->smbe_minor
);
135 smbios_bufopen_30(smbios_hdl_t
*shp
, const smbios_30_entry_t
*ep
, size_t len
,
138 if (strncmp(ep
->smbe_eanchor
, SMB3_ENTRY_EANCHOR
,
139 SMB3_ENTRY_EANCHORLEN
))
140 return (ESMB_HEADER
);
142 smb_dprintf(shp
, "opening SMBIOS version %u.%u\n",
143 ep
->smbe_major
, ep
->smbe_minor
);
145 if (!(flags
& SMB_O_NOVERS
)) {
146 if (ep
->smbe_major
> SMB_MAJOR(SMB_VERSION
))
149 if (ep
->smbe_major
< SMB_MAJOR(SMB_VERSION_23
) || (
150 ep
->smbe_major
== SMB_MAJOR(SMB_VERSION_23
) &&
151 ep
->smbe_minor
< SMB_MINOR(SMB_VERSION_23
)))
155 if (len
< sizeof (smb_header_t
) ||
156 ep
->smbe_stlen
< sizeof (smb_header_t
) || len
< ep
->smbe_stlen
)
159 if (!(flags
& SMB_O_NOCKSUM
)) {
161 const uchar_t
*p
, *q
;
164 for (p
= q
; p
< q
+ ep
->smbe_elen
; p
++)
168 smb_dprintf(shp
, "bad cksum: e=%x\n", esum
);
174 * Copy the entry point into our handle. The underlying entry point
175 * may be larger than our structure definition, so reset smbe_elen
176 * to our internal size and recompute good checksums for our copy.
178 shp
->sh_ent_type
= SMBIOS_ENTRY_POINT_30
;
179 bcopy(ep
, &shp
->sh_ent
, sizeof (smbios_entry_t
));
180 shp
->sh_ent
.ep30
.smbe_elen
= sizeof (smbios_entry_t
);
181 smbios_checksum(shp
, &shp
->sh_ent
);
183 shp
->sh_smbvers
= SMB_MAJMIN(ep
->smbe_major
, ep
->smbe_minor
);
189 smbios_table_nentries(const char *smbe_staddr
, uint32_t smbe_stlen
)
195 if (smbe_staddr
== NULL
)
198 for (dmi
= (char *)smbe_staddr
; dmi
< smbe_staddr
+ smbe_stlen
; i
++) {
199 hdr
= (smb_header_t
*)dmi
;
200 dmi
+= hdr
->smbh_len
;
202 * Search for the end of the string area.
204 while (dmi
+ 1 < smbe_staddr
+ smbe_stlen
&&
205 dmi
[0] != '\0' && dmi
[1] != '\0') {
214 smbios_bufopen(const smbios_entry_t
*ep
, const void *buf
, size_t len
,
215 int version
, int flags
, int *errp
)
217 smbios_hdl_t
*shp
= smb_zalloc(sizeof (smbios_hdl_t
));
218 const smb_header_t
*hp
, *nhp
;
219 const uchar_t
*p
, *q
, *s
;
234 return (smb_open_error(shp
, errp
, ESMB_VERSION
));
237 if (ep
== NULL
|| buf
== NULL
|| len
== 0 || (flags
& ~SMB_O_MASK
))
238 return (smb_open_error(shp
, errp
, ESMB_INVAL
));
241 return (smb_open_error(shp
, errp
, ESMB_NOMEM
));
244 shp
->sh_flags
|= SMB_FL_DEBUG
;
246 err
= smbios_bufopen_21(shp
, &ep
->ep21
, len
, flags
);
248 err
= smbios_bufopen_30(shp
, &ep
->ep30
, len
, flags
);
250 return (smb_open_error(shp
, errp
, err
));
252 smbios_table_nentries(buf
, ep
->ep30
.smbe_stlen
);
256 shp
->sh_buflen
= len
;
257 shp
->sh_structs
= smb_alloc(sizeof (smb_struct_t
) * shp
->sh_ent_stnum
);
258 shp
->sh_nstructs
= 0;
259 shp
->sh_hashlen
= _smb_hashlen
;
260 shp
->sh_hash
= smb_zalloc(sizeof (smb_struct_t
*) * shp
->sh_hashlen
);
261 shp
->sh_libvers
= version
;
263 if (shp
->sh_structs
== NULL
|| shp
->sh_hash
== NULL
)
264 return (smb_open_error(shp
, errp
, ESMB_NOMEM
));
267 switch (shp
->sh_ent_type
) {
268 case SMBIOS_ENTRY_POINT_21
:
269 q
= (const uchar_t
*)buf
+ MIN(ep
->ep21
.smbe_stlen
, len
);
271 case SMBIOS_ENTRY_POINT_30
:
272 q
= (const uchar_t
*)buf
+ MIN(ep
->ep30
.smbe_stlen
, len
);
275 return (smb_open_error(shp
, errp
, ESMB_VERSION
));
278 for (i
= 0; i
< shp
->sh_ent_stnum
; i
++, hp
= nhp
) {
279 smb_struct_t
*stp
= &shp
->sh_structs
[i
];
282 if ((const uchar_t
*)hp
+ sizeof (smb_header_t
) > q
) {
283 shp
->sh_flags
|= SMB_FL_TRUNC
;
287 smb_dprintf(shp
, "struct [%u] type %u len %u hdl %u at %p\n",
288 i
, hp
->smbh_type
, hp
->smbh_len
, hp
->smbh_hdl
, (void *)hp
);
290 if (hp
->smbh_type
== SMB_TYPE_EOT
)
291 break; /* ignore any entries beyond end-of-table */
293 if ((const uchar_t
*)hp
+ hp
->smbh_len
> q
- 2) {
294 shp
->sh_flags
|= SMB_FL_TRUNC
;
298 h
= hp
->smbh_hdl
& (shp
->sh_hashlen
- 1);
299 p
= s
= (const uchar_t
*)hp
+ hp
->smbh_len
;
301 while (p
<= q
- 2 && (p
[0] != '\0' || p
[1] != '\0')) {
303 n
++; /* count strings until \0\0 delimiter */
307 shp
->sh_flags
|= SMB_FL_TRUNC
;
312 n
++; /* add one for final string in string table */
317 stp
->smbst_next
= shp
->sh_hash
[h
];
318 stp
->smbst_strtab
= smb_alloc(sizeof (uint16_t) * n
);
319 stp
->smbst_strtablen
= n
;
321 if (n
!= 0 && stp
->smbst_strtab
== NULL
)
322 return (smb_open_error(shp
, errp
, ESMB_NOMEM
));
324 shp
->sh_hash
[h
] = stp
;
325 nhp
= (void *)(p
+ 2);
328 for (n
= 0, p
= s
; n
< stp
->smbst_strtablen
; p
++) {
330 stp
->smbst_strtab
[n
++] =
331 (uint16_t)(s
- stp
->smbst_str
);
337 /* error out if we couldn't find any complete entries in the table */
338 if ((shp
->sh_flags
& SMB_FL_TRUNC
) && i
== 0)
339 return (smb_open_error(shp
, errp
, ESMB_CORRUPT
));
341 if (flags
& SMB_O_ZIDS
)
348 smbios_close(smbios_hdl_t
*shp
)
352 for (i
= 0; i
< shp
->sh_nstructs
; i
++) {
353 smb_free(shp
->sh_structs
[i
].smbst_strtab
,
354 sizeof (uint16_t) * shp
->sh_structs
[i
].smbst_strtablen
);
357 smb_free(shp
->sh_structs
, sizeof (smb_struct_t
) * shp
->sh_ent_stnum
);
358 smb_free(shp
->sh_hash
, sizeof (smb_struct_t
*) * shp
->sh_hashlen
);
360 if (shp
->sh_flags
& SMB_FL_BUFALLOC
)
361 smb_free((void *)shp
->sh_buf
, shp
->sh_buflen
);
363 smb_free(shp
, sizeof (smbios_hdl_t
));
367 * Recompute the values of the entry point checksums based upon the content
368 * of the specified SMBIOS entry point. We don't need 'shp' but require it
369 * anyway in case future versioning requires variations in the algorithm.
373 smbios_checksum(smbios_hdl_t
*shp
, smbios_entry_t
*ep
)
375 uchar_t
*p
, *q
= (uchar_t
*)ep
;
376 uint8_t esum
= 0, isum
= 0;
378 switch (shp
->sh_ent_type
) {
379 case SMBIOS_ENTRY_POINT_21
:
380 ep
->ep21
.smbe_ecksum
= ep
->ep21
.smbe_icksum
= 0;
382 for (p
= (uchar_t
*)ep
->ep21
.smbe_ianchor
;
383 p
< q
+ sizeof (*ep
); p
++) {
387 ep
->ep21
.smbe_icksum
= -isum
;
389 for (p
= q
; p
< q
+ ep
->ep21
.smbe_elen
; p
++)
392 ep
->ep21
.smbe_ecksum
= -esum
;
394 case SMBIOS_ENTRY_POINT_30
:
395 ep
->ep30
.smbe_ecksum
= 0;
396 for (p
= q
; p
< q
+ ep
->ep30
.smbe_elen
; p
++)
399 ep
->ep30
.smbe_ecksum
= -esum
;
407 smbios_buf(smbios_hdl_t
*shp
)
409 return (shp
->sh_buf
);
413 smbios_buflen(smbios_hdl_t
*shp
)
415 return (shp
->sh_buflen
);
418 static smbios_struct_t
*
419 smb_export(const smb_struct_t
*stp
, smbios_struct_t
*sp
)
421 const smb_header_t
*hdr
= stp
->smbst_hdr
;
423 sp
->smbstr_id
= hdr
->smbh_hdl
;
424 sp
->smbstr_type
= hdr
->smbh_type
;
425 sp
->smbstr_data
= hdr
;
426 sp
->smbstr_size
= (size_t)(stp
->smbst_end
- (uchar_t
*)hdr
);
432 smbios_lookup_id(smbios_hdl_t
*shp
, id_t id
, smbios_struct_t
*sp
)
434 const smb_struct_t
*stp
= smb_lookup_id(shp
, id
);
437 return (-1); /* errno is set for us */
440 (void) smb_export(stp
, sp
);
446 smbios_lookup_type(smbios_hdl_t
*shp
, uint_t type
, smbios_struct_t
*sp
)
448 const smb_struct_t
*stp
= smb_lookup_type(shp
, type
);
451 return (-1); /* errno is set for us */
454 (void) smb_export(stp
, sp
);
460 smbios_iter(smbios_hdl_t
*shp
, smbios_struct_f
*func
, void *data
)
462 const smb_struct_t
*sp
= shp
->sh_structs
;
466 for (i
= 0; i
< shp
->sh_nstructs
; i
++, sp
++) {
467 if (sp
->smbst_hdr
->smbh_type
!= SMB_TYPE_INACTIVE
&&
468 (rv
= func(shp
, smb_export(sp
, &s
), data
)) != 0)
476 smb_lookup_type(smbios_hdl_t
*shp
, uint_t type
)
480 for (i
= 0; i
< shp
->sh_nstructs
; i
++) {
481 if (shp
->sh_structs
[i
].smbst_hdr
->smbh_type
== type
)
482 return (&shp
->sh_structs
[i
]);
485 (void) smb_set_errno(shp
, ESMB_NOENT
);
490 smb_lookup_id(smbios_hdl_t
*shp
, uint_t id
)
492 const smb_struct_t
*stp
= shp
->sh_hash
[id
& (shp
->sh_hashlen
- 1)];
496 (void) smb_set_errno(shp
, ESMB_NOTSUP
);
499 (void) smb_set_errno(shp
, ESMB_NOENT
);
503 for (; stp
!= NULL
; stp
= stp
->smbst_next
) {
504 if (stp
->smbst_hdr
->smbh_hdl
== id
)
509 (void) smb_set_errno(shp
, ESMB_NOENT
);
515 smb_strptr(const smb_struct_t
*stp
, uint_t i
)
517 if (i
== 0 || i
> stp
->smbst_strtablen
)
518 return (_smb_emptystr
);
520 return ((char *)stp
->smbst_str
+ stp
->smbst_strtab
[i
- 1]);
524 smb_libgteq(smbios_hdl_t
*shp
, int version
)
526 return (SMB_MAJOR(shp
->sh_libvers
) > SMB_MAJOR(version
) || (
527 SMB_MAJOR(shp
->sh_libvers
) == SMB_MAJOR(version
) &&
528 SMB_MINOR(shp
->sh_libvers
) >= SMB_MINOR(version
)));
532 smb_gteq(smbios_hdl_t
*shp
, int version
)
534 return (SMB_MAJOR(shp
->sh_smbvers
) > SMB_MAJOR(version
) || (
535 SMB_MAJOR(shp
->sh_smbvers
) == SMB_MAJOR(version
) &&
536 SMB_MINOR(shp
->sh_smbvers
) >= SMB_MINOR(version
)));
540 smbios_truncated(smbios_hdl_t
*shp
)
542 return ((shp
->sh_flags
& SMB_FL_TRUNC
) != 0);