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