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