1/* $OpenBSD: smbios.c,v 1.1 2022/12/07 23:04:26 patrick 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 21#include <machine/smbiosvar.h> 22 23#include <lib/libkern/libkern.h> 24#include <stand/boot/cmd.h> 25 26#include "libsa.h" 27 28#undef DPRINTF 29#if defined(SMBIOSDEBUG) 30#define DPRINTF(x...) do { printf(x); } while(0) 31#else 32#define DPRINTF(x...) 33#endif 34 35struct smbios_entry smbios_entry; 36 37const char *smbios_uninfo[] = { 38 "System", 39 "Not ", 40 "To be", 41 "SYS-" 42}; 43 44char smbios_bios_date[64]; 45char smbios_board_vendor[64]; 46char smbios_board_prod[64]; 47char smbios_board_serial[64]; 48 49void smbios_info(void); 50char *fixstring(char *); 51 52char *hw_vendor, *hw_prod, *hw_ver, *hw_serial; 53 54void 55smbios_init(void *smbios) 56{ 57 struct smbios_struct_bios *sb; 58 struct smbtable bios; 59 char scratch[64]; 60 char *sminfop; 61 uint64_t addr; 62 63 if (smbios == NULL) 64 return; 65 66 if (strncmp(smbios, "_SM_", 4) == 0) { 67 struct smbhdr *hdr = smbios; 68 uint8_t *p, checksum = 0; 69 int i; 70 71 if (hdr->len != sizeof(*hdr)) 72 return; 73 for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++) 74 checksum += p[i]; 75 if (checksum != 0) 76 return; 77 78 DPRINTF("SMBIOS %d.%d", hdr->majrev, hdr->minrev); 79 80 smbios_entry.len = hdr->size; 81 smbios_entry.mjr = hdr->majrev; 82 smbios_entry.min = hdr->minrev; 83 smbios_entry.count = hdr->count; 84 85 addr = hdr->addr; 86 } else if (strncmp(smbios, "_SM3_", 5) == 0) { 87 struct smb3hdr *hdr = smbios; 88 uint8_t *p, checksum = 0; 89 int i; 90 91 if (hdr->len != sizeof(*hdr) || hdr->epr != 0x01) 92 return; 93 for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++) 94 checksum += p[i]; 95 if (checksum != 0) 96 return; 97 98 DPRINTF("SMBIOS %d.%d.%d", hdr->majrev, hdr->minrev, 99 hdr->docrev); 100 101 smbios_entry.len = hdr->size; 102 smbios_entry.mjr = hdr->majrev; 103 smbios_entry.min = hdr->minrev; 104 smbios_entry.count = -1; 105 106 addr = hdr->addr; 107 } else { 108 DPRINTF("Unsupported SMBIOS entry point\n"); 109 return; 110 } 111 112 smbios_entry.addr = (uint8_t *)addr; 113 114 bios.cookie = 0; 115 if (smbios_find_table(SMBIOS_TYPE_BIOS, &bios)) { 116 sb = bios.tblhdr; 117 DPRINTF("SMBIOS:"); 118 if ((smbios_get_string(&bios, sb->vendor, 119 scratch, sizeof(scratch))) != NULL) 120 DPRINTF(" vendor %s", 121 fixstring(scratch)); 122 if ((smbios_get_string(&bios, sb->version, 123 scratch, sizeof(scratch))) != NULL) 124 DPRINTF(" version \"%s\"", 125 fixstring(scratch)); 126 if ((smbios_get_string(&bios, sb->release, 127 scratch, sizeof(scratch))) != NULL) { 128 sminfop = fixstring(scratch); 129 if (sminfop != NULL) { 130 strlcpy(smbios_bios_date, 131 sminfop, 132 sizeof(smbios_bios_date)); 133 DPRINTF(" date %s", sminfop); 134 } 135 } 136 137 smbios_info(); 138 DPRINTF("\n"); 139 } 140 141 return; 142} 143 144/* 145 * smbios_find_table() takes a caller supplied smbios struct type and 146 * a pointer to a handle (struct smbtable) returning one if the structure 147 * is successfully located and zero otherwise. Callers should take care 148 * to initialize the cookie field of the smbtable structure to zero before 149 * the first invocation of this function. 150 * Multiple tables of the same type can be located by repeatedly calling 151 * smbios_find_table with the same arguments. 152 */ 153int 154smbios_find_table(uint8_t type, struct smbtable *st) 155{ 156 uint8_t *va, *end; 157 struct smbtblhdr *hdr; 158 int ret = 0, tcount = 1; 159 160 va = smbios_entry.addr; 161 end = va + smbios_entry.len; 162 163 /* 164 * The cookie field of the smtable structure is used to locate 165 * multiple instances of a table of an arbitrary type. Following the 166 * successful location of a table, the type is encoded as bits 0:7 of 167 * the cookie value, the offset in terms of the number of structures 168 * preceding that referenced by the handle is encoded in bits 15:31. 169 */ 170 if ((st->cookie & 0xfff) == type && st->cookie >> 16) { 171 if ((uint8_t *)st->hdr >= va && (uint8_t *)st->hdr < end) { 172 hdr = st->hdr; 173 if (hdr->type == type) { 174 va = (uint8_t *)hdr + hdr->size; 175 for (; va + 1 < end; va++) 176 if (*va == 0 && *(va + 1) == 0) 177 break; 178 va += 2; 179 tcount = st->cookie >> 16; 180 } 181 } 182 } 183 for (; va + sizeof(struct smbtblhdr) < end && 184 tcount <= smbios_entry.count; tcount++) { 185 hdr = (struct smbtblhdr *)va; 186 if (hdr->type == type) { 187 ret = 1; 188 st->hdr = hdr; 189 st->tblhdr = va + sizeof(struct smbtblhdr); 190 st->cookie = (tcount + 1) << 16 | type; 191 break; 192 } 193 if (hdr->type == SMBIOS_TYPE_EOT) 194 break; 195 va += hdr->size; 196 for (; va + 1 < end; va++) 197 if (*va == 0 && *(va + 1) == 0) 198 break; 199 va += 2; 200 } 201 return ret; 202} 203 204char * 205smbios_get_string(struct smbtable *st, uint8_t indx, char *dest, size_t len) 206{ 207 uint8_t *va, *end; 208 char *ret = NULL; 209 int i; 210 211 va = (uint8_t *)st->hdr + st->hdr->size; 212 end = smbios_entry.addr + smbios_entry.len; 213 for (i = 1; va < end && i < indx && *va; i++) 214 while (*va++) 215 ; 216 if (i == indx) { 217 if (va + len < end) { 218 ret = dest; 219 memcpy(ret, va, len); 220 ret[len - 1] = '\0'; 221 } 222 } 223 224 return ret; 225} 226 227char * 228fixstring(char *s) 229{ 230 char *p, *e; 231#if 0 232 int i; 233 234 for (i = 0; i < nitems(smbios_uninfo); i++) 235 if ((strncasecmp(s, smbios_uninfo[i], 236 strlen(smbios_uninfo[i]))) == 0) 237 return NULL; 238#endif 239 /* 240 * Remove leading and trailing whitespace 241 */ 242 for (p = s; *p == ' '; p++) 243 ; 244 /* 245 * Special case entire string is whitespace 246 */ 247 if (p == s + strlen(s)) 248 return NULL; 249 for (e = s + strlen(s) - 1; e > s && *e == ' '; e--) 250 ; 251 if (p > s || e < s + strlen(s) - 1) { 252 memmove(s, p, e - p + 1); 253 s[e - p + 1] = '\0'; 254 } 255 256 return s; 257} 258 259void 260smbios_info(void) 261{ 262 char *sminfop, sminfo[64]; 263 struct smbtable stbl, btbl; 264 struct smbios_sys *sys; 265 struct smbios_board *board; 266 int infolen, havebb; 267 char *p; 268 269 if (smbios_entry.mjr < 2) 270 return; 271 /* 272 * According to the spec the system table among others is required, 273 * if it is not we do not bother with this smbios implementation. 274 */ 275 stbl.cookie = btbl.cookie = 0; 276 if (!smbios_find_table(SMBIOS_TYPE_SYSTEM, &stbl)) 277 return; 278 havebb = smbios_find_table(SMBIOS_TYPE_BASEBOARD, &btbl); 279 280 sys = (struct smbios_sys *)stbl.tblhdr; 281 if (havebb) { 282 board = (struct smbios_board *)btbl.tblhdr; 283 284 sminfop = NULL; 285 if ((p = smbios_get_string(&btbl, board->vendor, 286 sminfo, sizeof(sminfo))) != NULL) 287 sminfop = fixstring(p); 288 if (sminfop) 289 strlcpy(smbios_board_vendor, sminfop, 290 sizeof(smbios_board_vendor)); 291 292 sminfop = NULL; 293 if ((p = smbios_get_string(&btbl, board->product, 294 sminfo, sizeof(sminfo))) != NULL) 295 sminfop = fixstring(p); 296 if (sminfop) 297 strlcpy(smbios_board_prod, sminfop, 298 sizeof(smbios_board_prod)); 299 300 sminfop = NULL; 301 if ((p = smbios_get_string(&btbl, board->serial, 302 sminfo, sizeof(sminfo))) != NULL) 303 sminfop = fixstring(p); 304 if (sminfop) 305 strlcpy(smbios_board_serial, sminfop, 306 sizeof(smbios_board_serial)); 307 } 308 /* 309 * Some smbios implementations have no system vendor or 310 * product strings, some have very uninformative data which is 311 * harder to work around and we must rely upon various 312 * heuristics to detect this. In both cases we attempt to fall 313 * back on the base board information in the perhaps naive 314 * belief that motherboard vendors will supply this 315 * information. 316 */ 317 sminfop = NULL; 318 if ((p = smbios_get_string(&stbl, sys->vendor, sminfo, 319 sizeof(sminfo))) != NULL) 320 sminfop = fixstring(p); 321 if (sminfop == NULL) { 322 if (havebb) { 323 if ((p = smbios_get_string(&btbl, board->vendor, 324 sminfo, sizeof(sminfo))) != NULL) 325 sminfop = fixstring(p); 326 } 327 } 328 if (sminfop) { 329 infolen = strlen(sminfop) + 1; 330 hw_vendor = alloc(infolen); 331 if (hw_vendor) 332 strlcpy(hw_vendor, sminfop, infolen); 333 sminfop = NULL; 334 } 335 if ((p = smbios_get_string(&stbl, sys->product, sminfo, 336 sizeof(sminfo))) != NULL) 337 sminfop = fixstring(p); 338 if (sminfop == NULL) { 339 if (havebb) { 340 if ((p = smbios_get_string(&btbl, board->product, 341 sminfo, sizeof(sminfo))) != NULL) 342 sminfop = fixstring(p); 343 } 344 } 345 if (sminfop) { 346 infolen = strlen(sminfop) + 1; 347 hw_prod = alloc(infolen); 348 if (hw_prod) 349 strlcpy(hw_prod, sminfop, infolen); 350 sminfop = NULL; 351 } 352 if (hw_vendor != NULL && hw_prod != NULL) 353 DPRINTF("\nSMBIOS: %s %s", hw_vendor, hw_prod); 354 if ((p = smbios_get_string(&stbl, sys->version, sminfo, 355 sizeof(sminfo))) != NULL) 356 sminfop = fixstring(p); 357 if (sminfop) { 358 infolen = strlen(sminfop) + 1; 359 hw_ver = alloc(infolen); 360 if (hw_ver) 361 strlcpy(hw_ver, sminfop, infolen); 362 sminfop = NULL; 363 } 364 if ((p = smbios_get_string(&stbl, sys->serial, sminfo, 365 sizeof(sminfo))) != NULL) 366 sminfop = fixstring(p); 367 if (sminfop) { 368 infolen = strlen(sminfop) + 1; 369 hw_serial = alloc(infolen); 370 if (hw_serial) 371 strlcpy(hw_serial, sminfop, infolen); 372 } 373} 374