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 30struct smbios_entry smbios_entry; 31 32const char *smbios_uninfo[] = { 33 "System", 34 "Not ", 35 "To be", 36 "SYS-" 37}; 38 39char smbios_bios_date[64]; 40char smbios_board_vendor[64]; 41char smbios_board_prod[64]; 42char smbios_board_serial[64]; 43 44void smbios_info(char *); 45char *fixstring(char *); 46 47struct smbios_softc { 48 struct device sc_dev; 49 bus_space_tag_t sc_iot; 50}; 51 52int smbios_match(struct device *, void *, void *); 53void smbios_attach(struct device *, struct device *, void *); 54 55const struct cfattach smbios_ca = { 56 sizeof(struct device), smbios_match, smbios_attach 57}; 58 59struct cfdriver smbios_cd = { 60 NULL, "smbios", DV_DULL 61}; 62 63int 64smbios_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 71void 72smbios_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 */ 218int 219smbios_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 269char * 270smbios_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 292char * 293fixstring(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 322void 323smbios_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