1 /* $OpenBSD: smbios.c,v 1.8 2022/10/29 20:35:50 kettenis 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 #include <sys/device.h>
21 #include <sys/malloc.h>
22 #include <sys/systm.h>
23
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 #include <machine/smbiosvar.h>
27
28 #include <dev/ofw/fdt.h>
29
30 struct smbios_entry smbios_entry;
31
32 const char *smbios_uninfo[] = {
33 "System",
34 "Not ",
35 "To be",
36 "SYS-"
37 };
38
39 char smbios_bios_date[64];
40 char smbios_board_vendor[64];
41 char smbios_board_prod[64];
42 char smbios_board_serial[64];
43
44 void smbios_info(char *);
45 char *fixstring(char *);
46
47 struct smbios_softc {
48 struct device sc_dev;
49 bus_space_tag_t sc_iot;
50 };
51
52 int smbios_match(struct device *, void *, void *);
53 void smbios_attach(struct device *, struct device *, void *);
54
55 const struct cfattach smbios_ca = {
56 sizeof(struct device), smbios_match, smbios_attach
57 };
58
59 struct cfdriver smbios_cd = {
60 NULL, "smbios", DV_DULL
61 };
62
63 int
smbios_match(struct device * parent,void * match,void * aux)64 smbios_match(struct device *parent, void *match, void *aux)
65 {
66 struct fdt_attach_args *faa = aux;
67
68 return (strcmp(faa->fa_name, "smbios") == 0);
69 }
70
71 void
smbios_attach(struct device * parent,struct device * self,void * aux)72 smbios_attach(struct device *parent, struct device *self, void *aux)
73 {
74 struct smbios_softc *sc = (struct smbios_softc *)self;
75 struct fdt_attach_args *faa = aux;
76 struct smbios_struct_bios *sb;
77 struct smbtable bios;
78 char scratch[64];
79 char sig[5];
80 char *sminfop;
81 bus_addr_t addr;
82 bus_size_t size;
83 bus_space_handle_t ioh;
84
85 sc->sc_iot = faa->fa_iot;
86 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, sizeof(sig),
87 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh)) {
88 printf(": can't map SMBIOS entry point structure\n");
89 return;
90 }
91 bus_space_read_region_1(sc->sc_iot, ioh, 0, sig, sizeof(sig));
92 bus_space_unmap(sc->sc_iot, ioh, sizeof(sig));
93
94 if (strncmp(sig, "_SM_", 4) == 0) {
95 struct smbhdr *hdr;
96 uint8_t *p, checksum = 0;
97 int i;
98
99 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, sizeof(*hdr),
100 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh)) {
101 printf(": can't map SMBIOS entry point structure\n");
102 return;
103 }
104
105 hdr = bus_space_vaddr(sc->sc_iot, ioh);
106 if (hdr->len != sizeof(*hdr)) {
107 bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
108 printf("\n");
109 return;
110 }
111 for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++)
112 checksum += p[i];
113 if (checksum != 0) {
114 bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
115 printf("\n");
116 return;
117 }
118
119 printf(": SMBIOS %d.%d", hdr->majrev, hdr->minrev);
120
121 smbios_entry.len = hdr->size;
122 smbios_entry.mjr = hdr->majrev;
123 smbios_entry.min = hdr->minrev;
124 smbios_entry.count = hdr->count;
125
126 addr = hdr->addr;
127 size = hdr->size;
128 bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
129 } else if (strncmp(sig, "_SM3_", 5) == 0) {
130 struct smb3hdr *hdr;
131 uint8_t *p, checksum = 0;
132 int i;
133
134 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, sizeof(*hdr),
135 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh)) {
136 printf(": can't map SMBIOS entry point structure\n");
137 return;
138 }
139
140 hdr = bus_space_vaddr(sc->sc_iot, ioh);
141 if (hdr->len != sizeof(*hdr) || hdr->epr != 0x01) {
142 bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
143 printf("\n");
144 return;
145 }
146 for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++)
147 checksum += p[i];
148 if (checksum != 0) {
149 bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
150 printf("\n");
151 return;
152 }
153
154 printf(": SMBIOS %d.%d.%d", hdr->majrev, hdr->minrev,
155 hdr->docrev);
156
157 smbios_entry.len = hdr->size;
158 smbios_entry.mjr = hdr->majrev;
159 smbios_entry.min = hdr->minrev;
160 smbios_entry.count = -1;
161
162 addr = hdr->addr;
163 size = hdr->size;
164 bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
165 } else {
166 printf(": unsupported SMBIOS entry point\n");
167 return;
168 }
169
170 if (bus_space_map(sc->sc_iot, addr, size,
171 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh)) {
172 printf(": can't map SMBIOS structure table\n");
173 return;
174 }
175 smbios_entry.addr = bus_space_vaddr(sc->sc_iot, ioh);
176
177 bios.cookie = 0;
178 if (smbios_find_table(SMBIOS_TYPE_BIOS, &bios)) {
179 sb = bios.tblhdr;
180 printf("\n%s:", sc->sc_dev.dv_xname);
181 if ((smbios_get_string(&bios, sb->vendor,
182 scratch, sizeof(scratch))) != NULL)
183 printf(" vendor %s",
184 fixstring(scratch));
185 if ((smbios_get_string(&bios, sb->version,
186 scratch, sizeof(scratch))) != NULL)
187 printf(" version \"%s\"",
188 fixstring(scratch));
189 if ((smbios_get_string(&bios, sb->release,
190 scratch, sizeof(scratch))) != NULL) {
191 sminfop = fixstring(scratch);
192 if (sminfop != NULL) {
193 strlcpy(smbios_bios_date,
194 sminfop,
195 sizeof(smbios_bios_date));
196 printf(" date %s", sminfop);
197 }
198 }
199
200 smbios_info(sc->sc_dev.dv_xname);
201 }
202
203 bus_space_unmap(sc->sc_iot, ioh, size);
204
205 printf("\n");
206 return;
207 }
208
209 /*
210 * smbios_find_table() takes a caller supplied smbios struct type and
211 * a pointer to a handle (struct smbtable) returning one if the structure
212 * is successfully located and zero otherwise. Callers should take care
213 * to initialize the cookie field of the smbtable structure to zero before
214 * the first invocation of this function.
215 * Multiple tables of the same type can be located by repeatedly calling
216 * smbios_find_table with the same arguments.
217 */
218 int
smbios_find_table(uint8_t type,struct smbtable * st)219 smbios_find_table(uint8_t type, struct smbtable *st)
220 {
221 uint8_t *va, *end;
222 struct smbtblhdr *hdr;
223 int ret = 0, tcount = 1;
224
225 va = smbios_entry.addr;
226 end = va + smbios_entry.len;
227
228 /*
229 * The cookie field of the smtable structure is used to locate
230 * multiple instances of a table of an arbitrary type. Following the
231 * successful location of a table, the type is encoded as bits 0:7 of
232 * the cookie value, the offset in terms of the number of structures
233 * preceding that referenced by the handle is encoded in bits 15:31.
234 */
235 if ((st->cookie & 0xfff) == type && st->cookie >> 16) {
236 if ((uint8_t *)st->hdr >= va && (uint8_t *)st->hdr < end) {
237 hdr = st->hdr;
238 if (hdr->type == type) {
239 va = (uint8_t *)hdr + hdr->size;
240 for (; va + 1 < end; va++)
241 if (*va == 0 && *(va + 1) == 0)
242 break;
243 va += 2;
244 tcount = st->cookie >> 16;
245 }
246 }
247 }
248 for (; va + sizeof(struct smbtblhdr) < end &&
249 tcount <= smbios_entry.count; tcount++) {
250 hdr = (struct smbtblhdr *)va;
251 if (hdr->type == type) {
252 ret = 1;
253 st->hdr = hdr;
254 st->tblhdr = va + sizeof(struct smbtblhdr);
255 st->cookie = (tcount + 1) << 16 | type;
256 break;
257 }
258 if (hdr->type == SMBIOS_TYPE_EOT)
259 break;
260 va += hdr->size;
261 for (; va + 1 < end; va++)
262 if (*va == 0 && *(va + 1) == 0)
263 break;
264 va += 2;
265 }
266 return ret;
267 }
268
269 char *
smbios_get_string(struct smbtable * st,uint8_t indx,char * dest,size_t len)270 smbios_get_string(struct smbtable *st, uint8_t indx, char *dest, size_t len)
271 {
272 uint8_t *va, *end;
273 char *ret = NULL;
274 int i;
275
276 va = (uint8_t *)st->hdr + st->hdr->size;
277 end = smbios_entry.addr + smbios_entry.len;
278 for (i = 1; va < end && i < indx && *va; i++)
279 while (*va++)
280 ;
281 if (i == indx) {
282 if (va + len < end) {
283 ret = dest;
284 memcpy(ret, va, len);
285 ret[len - 1] = '\0';
286 }
287 }
288
289 return ret;
290 }
291
292 char *
fixstring(char * s)293 fixstring(char *s)
294 {
295 char *p, *e;
296 int i;
297
298 for (i = 0; i < nitems(smbios_uninfo); i++)
299 if ((strncasecmp(s, smbios_uninfo[i],
300 strlen(smbios_uninfo[i]))) == 0)
301 return NULL;
302 /*
303 * Remove leading and trailing whitespace
304 */
305 for (p = s; *p == ' '; p++)
306 ;
307 /*
308 * Special case entire string is whitespace
309 */
310 if (p == s + strlen(s))
311 return NULL;
312 for (e = s + strlen(s) - 1; e > s && *e == ' '; e--)
313 ;
314 if (p > s || e < s + strlen(s) - 1) {
315 memmove(s, p, e - p + 1);
316 s[e - p + 1] = '\0';
317 }
318
319 return s;
320 }
321
322 void
smbios_info(char * str)323 smbios_info(char *str)
324 {
325 char *sminfop, sminfo[64];
326 struct smbtable stbl, btbl;
327 struct smbios_sys *sys;
328 struct smbios_board *board;
329 int i, infolen, uuidf, havebb;
330 char *p;
331
332 if (smbios_entry.mjr < 2)
333 return;
334 /*
335 * According to the spec the system table among others is required,
336 * if it is not we do not bother with this smbios implementation.
337 */
338 stbl.cookie = btbl.cookie = 0;
339 if (!smbios_find_table(SMBIOS_TYPE_SYSTEM, &stbl))
340 return;
341 havebb = smbios_find_table(SMBIOS_TYPE_BASEBOARD, &btbl);
342
343 sys = (struct smbios_sys *)stbl.tblhdr;
344 if (havebb) {
345 board = (struct smbios_board *)btbl.tblhdr;
346
347 sminfop = NULL;
348 if ((p = smbios_get_string(&btbl, board->vendor,
349 sminfo, sizeof(sminfo))) != NULL)
350 sminfop = fixstring(p);
351 if (sminfop)
352 strlcpy(smbios_board_vendor, sminfop,
353 sizeof(smbios_board_vendor));
354
355 sminfop = NULL;
356 if ((p = smbios_get_string(&btbl, board->product,
357 sminfo, sizeof(sminfo))) != NULL)
358 sminfop = fixstring(p);
359 if (sminfop)
360 strlcpy(smbios_board_prod, sminfop,
361 sizeof(smbios_board_prod));
362
363 sminfop = NULL;
364 if ((p = smbios_get_string(&btbl, board->serial,
365 sminfo, sizeof(sminfo))) != NULL)
366 sminfop = fixstring(p);
367 if (sminfop)
368 strlcpy(smbios_board_serial, sminfop,
369 sizeof(smbios_board_serial));
370 }
371 /*
372 * Some smbios implementations have no system vendor or
373 * product strings, some have very uninformative data which is
374 * harder to work around and we must rely upon various
375 * heuristics to detect this. In both cases we attempt to fall
376 * back on the base board information in the perhaps naive
377 * belief that motherboard vendors will supply this
378 * information.
379 */
380 sminfop = NULL;
381 if ((p = smbios_get_string(&stbl, sys->vendor, sminfo,
382 sizeof(sminfo))) != NULL)
383 sminfop = fixstring(p);
384 if (sminfop == NULL) {
385 if (havebb) {
386 if ((p = smbios_get_string(&btbl, board->vendor,
387 sminfo, sizeof(sminfo))) != NULL)
388 sminfop = fixstring(p);
389 }
390 }
391 if (sminfop) {
392 infolen = strlen(sminfop) + 1;
393 hw_vendor = malloc(infolen, M_DEVBUF, M_NOWAIT);
394 if (hw_vendor)
395 strlcpy(hw_vendor, sminfop, infolen);
396 sminfop = NULL;
397 }
398 if ((p = smbios_get_string(&stbl, sys->product, sminfo,
399 sizeof(sminfo))) != NULL)
400 sminfop = fixstring(p);
401 if (sminfop == NULL) {
402 if (havebb) {
403 if ((p = smbios_get_string(&btbl, board->product,
404 sminfo, sizeof(sminfo))) != NULL)
405 sminfop = fixstring(p);
406 }
407 }
408 if (sminfop) {
409 infolen = strlen(sminfop) + 1;
410 hw_prod = malloc(infolen, M_DEVBUF, M_NOWAIT);
411 if (hw_prod)
412 strlcpy(hw_prod, sminfop, infolen);
413 sminfop = NULL;
414 }
415 if (hw_vendor != NULL && hw_prod != NULL)
416 printf("\n%s: %s %s", str, hw_vendor, hw_prod);
417 if ((p = smbios_get_string(&stbl, sys->version, sminfo,
418 sizeof(sminfo))) != NULL)
419 sminfop = fixstring(p);
420 if (sminfop) {
421 infolen = strlen(sminfop) + 1;
422 hw_ver = malloc(infolen, M_DEVBUF, M_NOWAIT);
423 if (hw_ver)
424 strlcpy(hw_ver, sminfop, infolen);
425 sminfop = NULL;
426 }
427 if ((p = smbios_get_string(&stbl, sys->serial, sminfo,
428 sizeof(sminfo))) != NULL)
429 sminfop = fixstring(p);
430 if (sminfop) {
431 infolen = strlen(sminfop) + 1;
432 for (i = 0; i < infolen - 1; i++)
433 enqueue_randomness(sminfop[i]);
434 hw_serial = malloc(infolen, M_DEVBUF, M_NOWAIT);
435 if (hw_serial)
436 strlcpy(hw_serial, sminfop, infolen);
437 }
438 if (smbios_entry.mjr > 2 || (smbios_entry.mjr == 2 &&
439 smbios_entry.min >= 1)) {
440 /*
441 * If the uuid value is all 0xff the uuid is present but not
442 * set, if its all 0 then the uuid isn't present at all.
443 */
444 uuidf = SMBIOS_UUID_NPRESENT|SMBIOS_UUID_NSET;
445 for (i = 0; i < sizeof(sys->uuid); i++) {
446 if (sys->uuid[i] != 0xff)
447 uuidf &= ~SMBIOS_UUID_NSET;
448 if (sys->uuid[i] != 0)
449 uuidf &= ~SMBIOS_UUID_NPRESENT;
450 }
451
452 if (uuidf & SMBIOS_UUID_NPRESENT)
453 hw_uuid = NULL;
454 else if (uuidf & SMBIOS_UUID_NSET)
455 hw_uuid = "Not Set";
456 else {
457 for (i = 0; i < sizeof(sys->uuid); i++)
458 enqueue_randomness(sys->uuid[i]);
459 hw_uuid = malloc(SMBIOS_UUID_REPLEN, M_DEVBUF,
460 M_NOWAIT);
461 if (hw_uuid) {
462 snprintf(hw_uuid, SMBIOS_UUID_REPLEN,
463 SMBIOS_UUID_REP,
464 sys->uuid[0], sys->uuid[1], sys->uuid[2],
465 sys->uuid[3], sys->uuid[4], sys->uuid[5],
466 sys->uuid[6], sys->uuid[7], sys->uuid[8],
467 sys->uuid[9], sys->uuid[10], sys->uuid[11],
468 sys->uuid[12], sys->uuid[13], sys->uuid[14],
469 sys->uuid[15]);
470 }
471 }
472 }
473 }
474