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