1 /* $OpenBSD: smbios.c,v 1.1 2022/12/07 23:04:26 patrick Exp $ */
2 /*
3 * Copyright (c) 2006 Gordon Willem Klok <gklok@cogeco.ca>
4 * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20
21 #include <machine/smbiosvar.h>
22
23 #include <lib/libkern/libkern.h>
24 #include <stand/boot/cmd.h>
25
26 #include "libsa.h"
27
28 #undef DPRINTF
29 #if defined(SMBIOSDEBUG)
30 #define DPRINTF(x...) do { printf(x); } while(0)
31 #else
32 #define DPRINTF(x...)
33 #endif
34
35 struct smbios_entry smbios_entry;
36
37 const char *smbios_uninfo[] = {
38 "System",
39 "Not ",
40 "To be",
41 "SYS-"
42 };
43
44 char smbios_bios_date[64];
45 char smbios_board_vendor[64];
46 char smbios_board_prod[64];
47 char smbios_board_serial[64];
48
49 void smbios_info(void);
50 char *fixstring(char *);
51
52 char *hw_vendor, *hw_prod, *hw_ver, *hw_serial;
53
54 void
smbios_init(void * smbios)55 smbios_init(void *smbios)
56 {
57 struct smbios_struct_bios *sb;
58 struct smbtable bios;
59 char scratch[64];
60 char *sminfop;
61 uint64_t addr;
62
63 if (smbios == NULL)
64 return;
65
66 if (strncmp(smbios, "_SM_", 4) == 0) {
67 struct smbhdr *hdr = smbios;
68 uint8_t *p, checksum = 0;
69 int i;
70
71 if (hdr->len != sizeof(*hdr))
72 return;
73 for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++)
74 checksum += p[i];
75 if (checksum != 0)
76 return;
77
78 DPRINTF("SMBIOS %d.%d", hdr->majrev, hdr->minrev);
79
80 smbios_entry.len = hdr->size;
81 smbios_entry.mjr = hdr->majrev;
82 smbios_entry.min = hdr->minrev;
83 smbios_entry.count = hdr->count;
84
85 addr = hdr->addr;
86 } else if (strncmp(smbios, "_SM3_", 5) == 0) {
87 struct smb3hdr *hdr = smbios;
88 uint8_t *p, checksum = 0;
89 int i;
90
91 if (hdr->len != sizeof(*hdr) || hdr->epr != 0x01)
92 return;
93 for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++)
94 checksum += p[i];
95 if (checksum != 0)
96 return;
97
98 DPRINTF("SMBIOS %d.%d.%d", hdr->majrev, hdr->minrev,
99 hdr->docrev);
100
101 smbios_entry.len = hdr->size;
102 smbios_entry.mjr = hdr->majrev;
103 smbios_entry.min = hdr->minrev;
104 smbios_entry.count = -1;
105
106 addr = hdr->addr;
107 } else {
108 DPRINTF("Unsupported SMBIOS entry point\n");
109 return;
110 }
111
112 smbios_entry.addr = (uint8_t *)addr;
113
114 bios.cookie = 0;
115 if (smbios_find_table(SMBIOS_TYPE_BIOS, &bios)) {
116 sb = bios.tblhdr;
117 DPRINTF("SMBIOS:");
118 if ((smbios_get_string(&bios, sb->vendor,
119 scratch, sizeof(scratch))) != NULL)
120 DPRINTF(" vendor %s",
121 fixstring(scratch));
122 if ((smbios_get_string(&bios, sb->version,
123 scratch, sizeof(scratch))) != NULL)
124 DPRINTF(" version \"%s\"",
125 fixstring(scratch));
126 if ((smbios_get_string(&bios, sb->release,
127 scratch, sizeof(scratch))) != NULL) {
128 sminfop = fixstring(scratch);
129 if (sminfop != NULL) {
130 strlcpy(smbios_bios_date,
131 sminfop,
132 sizeof(smbios_bios_date));
133 DPRINTF(" date %s", sminfop);
134 }
135 }
136
137 smbios_info();
138 DPRINTF("\n");
139 }
140
141 return;
142 }
143
144 /*
145 * smbios_find_table() takes a caller supplied smbios struct type and
146 * a pointer to a handle (struct smbtable) returning one if the structure
147 * is successfully located and zero otherwise. Callers should take care
148 * to initialize the cookie field of the smbtable structure to zero before
149 * the first invocation of this function.
150 * Multiple tables of the same type can be located by repeatedly calling
151 * smbios_find_table with the same arguments.
152 */
153 int
smbios_find_table(uint8_t type,struct smbtable * st)154 smbios_find_table(uint8_t type, struct smbtable *st)
155 {
156 uint8_t *va, *end;
157 struct smbtblhdr *hdr;
158 int ret = 0, tcount = 1;
159
160 va = smbios_entry.addr;
161 end = va + smbios_entry.len;
162
163 /*
164 * The cookie field of the smtable structure is used to locate
165 * multiple instances of a table of an arbitrary type. Following the
166 * successful location of a table, the type is encoded as bits 0:7 of
167 * the cookie value, the offset in terms of the number of structures
168 * preceding that referenced by the handle is encoded in bits 15:31.
169 */
170 if ((st->cookie & 0xfff) == type && st->cookie >> 16) {
171 if ((uint8_t *)st->hdr >= va && (uint8_t *)st->hdr < end) {
172 hdr = st->hdr;
173 if (hdr->type == type) {
174 va = (uint8_t *)hdr + hdr->size;
175 for (; va + 1 < end; va++)
176 if (*va == 0 && *(va + 1) == 0)
177 break;
178 va += 2;
179 tcount = st->cookie >> 16;
180 }
181 }
182 }
183 for (; va + sizeof(struct smbtblhdr) < end &&
184 tcount <= smbios_entry.count; tcount++) {
185 hdr = (struct smbtblhdr *)va;
186 if (hdr->type == type) {
187 ret = 1;
188 st->hdr = hdr;
189 st->tblhdr = va + sizeof(struct smbtblhdr);
190 st->cookie = (tcount + 1) << 16 | type;
191 break;
192 }
193 if (hdr->type == SMBIOS_TYPE_EOT)
194 break;
195 va += hdr->size;
196 for (; va + 1 < end; va++)
197 if (*va == 0 && *(va + 1) == 0)
198 break;
199 va += 2;
200 }
201 return ret;
202 }
203
204 char *
smbios_get_string(struct smbtable * st,uint8_t indx,char * dest,size_t len)205 smbios_get_string(struct smbtable *st, uint8_t indx, char *dest, size_t len)
206 {
207 uint8_t *va, *end;
208 char *ret = NULL;
209 int i;
210
211 va = (uint8_t *)st->hdr + st->hdr->size;
212 end = smbios_entry.addr + smbios_entry.len;
213 for (i = 1; va < end && i < indx && *va; i++)
214 while (*va++)
215 ;
216 if (i == indx) {
217 if (va + len < end) {
218 ret = dest;
219 memcpy(ret, va, len);
220 ret[len - 1] = '\0';
221 }
222 }
223
224 return ret;
225 }
226
227 char *
fixstring(char * s)228 fixstring(char *s)
229 {
230 char *p, *e;
231 #if 0
232 int i;
233
234 for (i = 0; i < nitems(smbios_uninfo); i++)
235 if ((strncasecmp(s, smbios_uninfo[i],
236 strlen(smbios_uninfo[i]))) == 0)
237 return NULL;
238 #endif
239 /*
240 * Remove leading and trailing whitespace
241 */
242 for (p = s; *p == ' '; p++)
243 ;
244 /*
245 * Special case entire string is whitespace
246 */
247 if (p == s + strlen(s))
248 return NULL;
249 for (e = s + strlen(s) - 1; e > s && *e == ' '; e--)
250 ;
251 if (p > s || e < s + strlen(s) - 1) {
252 memmove(s, p, e - p + 1);
253 s[e - p + 1] = '\0';
254 }
255
256 return s;
257 }
258
259 void
smbios_info(void)260 smbios_info(void)
261 {
262 char *sminfop, sminfo[64];
263 struct smbtable stbl, btbl;
264 struct smbios_sys *sys;
265 struct smbios_board *board;
266 int infolen, havebb;
267 char *p;
268
269 if (smbios_entry.mjr < 2)
270 return;
271 /*
272 * According to the spec the system table among others is required,
273 * if it is not we do not bother with this smbios implementation.
274 */
275 stbl.cookie = btbl.cookie = 0;
276 if (!smbios_find_table(SMBIOS_TYPE_SYSTEM, &stbl))
277 return;
278 havebb = smbios_find_table(SMBIOS_TYPE_BASEBOARD, &btbl);
279
280 sys = (struct smbios_sys *)stbl.tblhdr;
281 if (havebb) {
282 board = (struct smbios_board *)btbl.tblhdr;
283
284 sminfop = NULL;
285 if ((p = smbios_get_string(&btbl, board->vendor,
286 sminfo, sizeof(sminfo))) != NULL)
287 sminfop = fixstring(p);
288 if (sminfop)
289 strlcpy(smbios_board_vendor, sminfop,
290 sizeof(smbios_board_vendor));
291
292 sminfop = NULL;
293 if ((p = smbios_get_string(&btbl, board->product,
294 sminfo, sizeof(sminfo))) != NULL)
295 sminfop = fixstring(p);
296 if (sminfop)
297 strlcpy(smbios_board_prod, sminfop,
298 sizeof(smbios_board_prod));
299
300 sminfop = NULL;
301 if ((p = smbios_get_string(&btbl, board->serial,
302 sminfo, sizeof(sminfo))) != NULL)
303 sminfop = fixstring(p);
304 if (sminfop)
305 strlcpy(smbios_board_serial, sminfop,
306 sizeof(smbios_board_serial));
307 }
308 /*
309 * Some smbios implementations have no system vendor or
310 * product strings, some have very uninformative data which is
311 * harder to work around and we must rely upon various
312 * heuristics to detect this. In both cases we attempt to fall
313 * back on the base board information in the perhaps naive
314 * belief that motherboard vendors will supply this
315 * information.
316 */
317 sminfop = NULL;
318 if ((p = smbios_get_string(&stbl, sys->vendor, sminfo,
319 sizeof(sminfo))) != NULL)
320 sminfop = fixstring(p);
321 if (sminfop == NULL) {
322 if (havebb) {
323 if ((p = smbios_get_string(&btbl, board->vendor,
324 sminfo, sizeof(sminfo))) != NULL)
325 sminfop = fixstring(p);
326 }
327 }
328 if (sminfop) {
329 infolen = strlen(sminfop) + 1;
330 hw_vendor = alloc(infolen);
331 if (hw_vendor)
332 strlcpy(hw_vendor, sminfop, infolen);
333 sminfop = NULL;
334 }
335 if ((p = smbios_get_string(&stbl, sys->product, sminfo,
336 sizeof(sminfo))) != NULL)
337 sminfop = fixstring(p);
338 if (sminfop == NULL) {
339 if (havebb) {
340 if ((p = smbios_get_string(&btbl, board->product,
341 sminfo, sizeof(sminfo))) != NULL)
342 sminfop = fixstring(p);
343 }
344 }
345 if (sminfop) {
346 infolen = strlen(sminfop) + 1;
347 hw_prod = alloc(infolen);
348 if (hw_prod)
349 strlcpy(hw_prod, sminfop, infolen);
350 sminfop = NULL;
351 }
352 if (hw_vendor != NULL && hw_prod != NULL)
353 DPRINTF("\nSMBIOS: %s %s", hw_vendor, hw_prod);
354 if ((p = smbios_get_string(&stbl, sys->version, sminfo,
355 sizeof(sminfo))) != NULL)
356 sminfop = fixstring(p);
357 if (sminfop) {
358 infolen = strlen(sminfop) + 1;
359 hw_ver = alloc(infolen);
360 if (hw_ver)
361 strlcpy(hw_ver, sminfop, infolen);
362 sminfop = NULL;
363 }
364 if ((p = smbios_get_string(&stbl, sys->serial, sminfo,
365 sizeof(sminfo))) != NULL)
366 sminfop = fixstring(p);
367 if (sminfop) {
368 infolen = strlen(sminfop) + 1;
369 hw_serial = alloc(infolen);
370 if (hw_serial)
371 strlcpy(hw_serial, sminfop, infolen);
372 }
373 }
374