1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 34 #include <assert.h> 35 #include <errno.h> 36 #include <md5.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 #include <uuid.h> 42 43 #include <machine/vmm.h> 44 #include <vmmapi.h> 45 46 #include "bhyverun.h" 47 #include "config.h" 48 #include "debug.h" 49 #include "smbiostbl.h" 50 51 #define MB (1024*1024) 52 #define GB (1024ULL*1024*1024) 53 54 #define SMBIOS_BASE 0xF1000 55 56 #define FIRMWARE_VERSION "14.0" 57 /* The SMBIOS specification defines the date format to be mm/dd/yyyy */ 58 #define FIRMWARE_RELEASE_DATE "10/10/2021" 59 60 /* BHYVE_ACPI_BASE - SMBIOS_BASE) */ 61 #define SMBIOS_MAX_LENGTH (0xF2400 - 0xF1000) 62 63 #define SMBIOS_TYPE_BIOS 0 64 #define SMBIOS_TYPE_SYSTEM 1 65 #define SMBIOS_TYPE_BOARD 2 66 #define SMBIOS_TYPE_CHASSIS 3 67 #define SMBIOS_TYPE_PROCESSOR 4 68 #define SMBIOS_TYPE_MEMARRAY 16 69 #define SMBIOS_TYPE_MEMDEVICE 17 70 #define SMBIOS_TYPE_MEMARRAYMAP 19 71 #define SMBIOS_TYPE_BOOT 32 72 #define SMBIOS_TYPE_EOT 127 73 74 struct smbios_structure { 75 uint8_t type; 76 uint8_t length; 77 uint16_t handle; 78 } __packed; 79 80 typedef int (*initializer_func_t)(struct smbios_structure *template_entry, 81 const char **template_strings, char *curaddr, char **endaddr, 82 uint16_t *n, uint16_t *size); 83 84 struct smbios_template_entry { 85 struct smbios_structure *entry; 86 const char **strings; 87 initializer_func_t initializer; 88 }; 89 90 /* 91 * SMBIOS Structure Table Entry Point 92 */ 93 #define SMBIOS_ENTRY_EANCHOR "_SM_" 94 #define SMBIOS_ENTRY_EANCHORLEN 4 95 #define SMBIOS_ENTRY_IANCHOR "_DMI_" 96 #define SMBIOS_ENTRY_IANCHORLEN 5 97 98 struct smbios_entry_point { 99 char eanchor[4]; /* anchor tag */ 100 uint8_t echecksum; /* checksum of entry point structure */ 101 uint8_t eplen; /* length in bytes of entry point */ 102 uint8_t major; /* major version of the SMBIOS spec */ 103 uint8_t minor; /* minor version of the SMBIOS spec */ 104 uint16_t maxssize; /* maximum size in bytes of a struct */ 105 uint8_t revision; /* entry point structure revision */ 106 uint8_t format[5]; /* entry point rev-specific data */ 107 char ianchor[5]; /* intermediate anchor tag */ 108 uint8_t ichecksum; /* intermediate checksum */ 109 uint16_t stlen; /* len in bytes of structure table */ 110 uint32_t staddr; /* physical addr of structure table */ 111 uint16_t stnum; /* number of structure table entries */ 112 uint8_t bcdrev; /* BCD value representing DMI ver */ 113 } __packed; 114 115 /* 116 * BIOS Information 117 */ 118 #define SMBIOS_FL_ISA 0x00000010 /* ISA is supported */ 119 #define SMBIOS_FL_PCI 0x00000080 /* PCI is supported */ 120 #define SMBIOS_FL_SHADOW 0x00001000 /* BIOS shadowing is allowed */ 121 #define SMBIOS_FL_CDBOOT 0x00008000 /* Boot from CD is supported */ 122 #define SMBIOS_FL_SELBOOT 0x00010000 /* Selectable Boot supported */ 123 #define SMBIOS_FL_EDD 0x00080000 /* EDD Spec is supported */ 124 125 #define SMBIOS_XB1_FL_ACPI 0x00000001 /* ACPI is supported */ 126 127 #define SMBIOS_XB2_FL_BBS 0x00000001 /* BIOS Boot Specification */ 128 #define SMBIOS_XB2_FL_VM 0x00000010 /* Virtual Machine */ 129 130 struct smbios_table_type0 { 131 struct smbios_structure header; 132 uint8_t vendor; /* vendor string */ 133 uint8_t version; /* version string */ 134 uint16_t segment; /* address segment location */ 135 uint8_t rel_date; /* release date */ 136 uint8_t size; /* rom size */ 137 uint64_t cflags; /* characteristics */ 138 uint8_t xc_bytes[2]; /* characteristics ext bytes */ 139 uint8_t sb_major_rel; /* system bios version */ 140 uint8_t sb_minor_rele; 141 uint8_t ecfw_major_rel; /* embedded ctrl fw version */ 142 uint8_t ecfw_minor_rel; 143 } __packed; 144 145 /* 146 * System Information 147 */ 148 #define SMBIOS_WAKEUP_SWITCH 0x06 /* power switch */ 149 150 struct smbios_table_type1 { 151 struct smbios_structure header; 152 uint8_t manufacturer; /* manufacturer string */ 153 uint8_t product; /* product name string */ 154 uint8_t version; /* version string */ 155 uint8_t serial; /* serial number string */ 156 uint8_t uuid[16]; /* uuid byte array */ 157 uint8_t wakeup; /* wake-up event */ 158 uint8_t sku; /* sku number string */ 159 uint8_t family; /* family name string */ 160 } __packed; 161 162 /* 163 * Baseboard (or Module) Information 164 */ 165 #define SMBIOS_BRF_HOSTING 0x1 166 #define SMBIOS_BRT_MOTHERBOARD 0xa 167 168 struct smbios_table_type2 { 169 struct smbios_structure header; 170 uint8_t manufacturer; /* manufacturer string */ 171 uint8_t product; /* product name string */ 172 uint8_t version; /* version string */ 173 uint8_t serial; /* serial number string */ 174 uint8_t asset; /* asset tag string */ 175 uint8_t fflags; /* feature flags */ 176 uint8_t location; /* location in chassis */ 177 uint16_t chandle; /* chassis handle */ 178 uint8_t type; /* board type */ 179 uint8_t n_objs; /* number of contained object handles */ 180 } __packed; 181 182 /* 183 * System Enclosure or Chassis 184 */ 185 #define SMBIOS_CHT_UNKNOWN 0x02 /* unknown */ 186 #define SMBIOS_CHT_DESKTOP 0x03 /* desktop */ 187 188 #define SMBIOS_CHST_SAFE 0x03 /* safe */ 189 190 #define SMBIOS_CHSC_NONE 0x03 /* none */ 191 192 struct smbios_table_type3 { 193 struct smbios_structure header; 194 uint8_t manufacturer; /* manufacturer string */ 195 uint8_t type; /* type */ 196 uint8_t version; /* version string */ 197 uint8_t serial; /* serial number string */ 198 uint8_t asset; /* asset tag string */ 199 uint8_t bustate; /* boot-up state */ 200 uint8_t psstate; /* power supply state */ 201 uint8_t tstate; /* thermal state */ 202 uint8_t security; /* security status */ 203 uint32_t oemdata; /* OEM-specific data */ 204 uint8_t uheight; /* height in 'u's */ 205 uint8_t cords; /* number of power cords */ 206 uint8_t elems; /* number of element records */ 207 uint8_t elemlen; /* length of records */ 208 uint8_t sku; /* sku number string */ 209 } __packed; 210 211 /* 212 * Processor Information 213 */ 214 #define SMBIOS_PRT_CENTRAL 0x03 /* central processor */ 215 216 #define SMBIOS_PRF_OTHER 0x01 /* other */ 217 218 #define SMBIOS_PRS_PRESENT 0x40 /* socket is populated */ 219 #define SMBIOS_PRS_ENABLED 0x1 /* enabled */ 220 221 #define SMBIOS_PRU_NONE 0x06 /* none */ 222 223 #define SMBIOS_PFL_64B 0x04 /* 64-bit capable */ 224 225 struct smbios_table_type4 { 226 struct smbios_structure header; 227 uint8_t socket; /* socket designation string */ 228 uint8_t type; /* processor type */ 229 uint8_t family; /* processor family */ 230 uint8_t manufacturer; /* manufacturer string */ 231 uint64_t cpuid; /* processor cpuid */ 232 uint8_t version; /* version string */ 233 uint8_t voltage; /* voltage */ 234 uint16_t clkspeed; /* ext clock speed in mhz */ 235 uint16_t maxspeed; /* maximum speed in mhz */ 236 uint16_t curspeed; /* current speed in mhz */ 237 uint8_t status; /* status */ 238 uint8_t upgrade; /* upgrade */ 239 uint16_t l1handle; /* l1 cache handle */ 240 uint16_t l2handle; /* l2 cache handle */ 241 uint16_t l3handle; /* l3 cache handle */ 242 uint8_t serial; /* serial number string */ 243 uint8_t asset; /* asset tag string */ 244 uint8_t part; /* part number string */ 245 uint8_t cores; /* cores per socket */ 246 uint8_t ecores; /* enabled cores */ 247 uint8_t threads; /* threads per socket */ 248 uint16_t cflags; /* processor characteristics */ 249 uint16_t family2; /* processor family 2 */ 250 } __packed; 251 252 /* 253 * Physical Memory Array 254 */ 255 #define SMBIOS_MAL_SYSMB 0x03 /* system board or motherboard */ 256 257 #define SMBIOS_MAU_SYSTEM 0x03 /* system memory */ 258 259 #define SMBIOS_MAE_NONE 0x03 /* none */ 260 261 struct smbios_table_type16 { 262 struct smbios_structure header; 263 uint8_t location; /* physical device location */ 264 uint8_t use; /* device functional purpose */ 265 uint8_t ecc; /* err detect/correct method */ 266 uint32_t size; /* max mem capacity in kb */ 267 uint16_t errhand; /* handle of error (if any) */ 268 uint16_t ndevs; /* num of slots or sockets */ 269 uint64_t xsize; /* max mem capacity in bytes */ 270 } __packed; 271 272 /* 273 * Memory Device 274 */ 275 #define SMBIOS_MDFF_UNKNOWN 0x02 /* unknown */ 276 277 #define SMBIOS_MDT_UNKNOWN 0x02 /* unknown */ 278 279 #define SMBIOS_MDF_UNKNOWN 0x0004 /* unknown */ 280 281 struct smbios_table_type17 { 282 struct smbios_structure header; 283 uint16_t arrayhand; /* handle of physl mem array */ 284 uint16_t errhand; /* handle of mem error data */ 285 uint16_t twidth; /* total width in bits */ 286 uint16_t dwidth; /* data width in bits */ 287 uint16_t size; /* size in kb or mb */ 288 uint8_t form; /* form factor */ 289 uint8_t set; /* set */ 290 uint8_t dloc; /* device locator string */ 291 uint8_t bloc; /* phys bank locator string */ 292 uint8_t type; /* memory type */ 293 uint16_t flags; /* memory characteristics */ 294 uint16_t maxspeed; /* maximum speed in mhz */ 295 uint8_t manufacturer; /* manufacturer string */ 296 uint8_t serial; /* serial number string */ 297 uint8_t asset; /* asset tag string */ 298 uint8_t part; /* part number string */ 299 uint8_t attributes; /* attributes */ 300 uint32_t xsize; /* extended size in mb */ 301 uint16_t curspeed; /* current speed in mhz */ 302 uint16_t minvoltage; /* minimum voltage */ 303 uint16_t maxvoltage; /* maximum voltage */ 304 uint16_t curvoltage; /* configured voltage */ 305 } __packed; 306 307 /* 308 * Memory Array Mapped Address 309 */ 310 struct smbios_table_type19 { 311 struct smbios_structure header; 312 uint32_t saddr; /* start phys addr in kb */ 313 uint32_t eaddr; /* end phys addr in kb */ 314 uint16_t arrayhand; /* physical mem array handle */ 315 uint8_t width; /* num of dev in row */ 316 uint64_t xsaddr; /* start phys addr in bytes */ 317 uint64_t xeaddr; /* end phys addr in bytes */ 318 } __packed; 319 320 /* 321 * System Boot Information 322 */ 323 #define SMBIOS_BOOT_NORMAL 0 /* no errors detected */ 324 325 struct smbios_table_type32 { 326 struct smbios_structure header; 327 uint8_t reserved[6]; 328 uint8_t status; /* boot status */ 329 } __packed; 330 331 /* 332 * End-of-Table 333 */ 334 struct smbios_table_type127 { 335 struct smbios_structure header; 336 } __packed; 337 338 struct smbios_table_type0 smbios_type0_template = { 339 { SMBIOS_TYPE_BIOS, sizeof (struct smbios_table_type0), 0 }, 340 1, /* bios vendor string */ 341 2, /* bios version string */ 342 0xF000, /* bios address segment location */ 343 3, /* bios release date */ 344 0x0, /* bios size (64k * (n + 1) is the size in bytes) */ 345 SMBIOS_FL_ISA | SMBIOS_FL_PCI | SMBIOS_FL_SHADOW | 346 SMBIOS_FL_CDBOOT | SMBIOS_FL_EDD, 347 { SMBIOS_XB1_FL_ACPI, SMBIOS_XB2_FL_BBS | SMBIOS_XB2_FL_VM }, 348 0x0, /* bios major release */ 349 0x0, /* bios minor release */ 350 0xff, /* embedded controller firmware major release */ 351 0xff /* embedded controller firmware minor release */ 352 }; 353 354 const char *smbios_type0_strings[] = { 355 "BHYVE", /* vendor string */ 356 FIRMWARE_VERSION, /* bios version string */ 357 FIRMWARE_RELEASE_DATE, /* bios release date string */ 358 NULL 359 }; 360 361 struct smbios_table_type1 smbios_type1_template = { 362 { SMBIOS_TYPE_SYSTEM, sizeof (struct smbios_table_type1), 0 }, 363 1, /* manufacturer string */ 364 2, /* product string */ 365 3, /* version string */ 366 4, /* serial number string */ 367 { 0 }, 368 SMBIOS_WAKEUP_SWITCH, 369 5, /* sku string */ 370 6 /* family string */ 371 }; 372 373 static int smbios_type1_initializer(struct smbios_structure *template_entry, 374 const char **template_strings, char *curaddr, char **endaddr, 375 uint16_t *n, uint16_t *size); 376 377 const char *smbios_type1_strings[] = { 378 "illumos", /* manufacturer string */ 379 "BHYVE", /* product name string */ 380 "1.0", /* version string */ 381 "None", /* serial number string */ 382 "None", /* sku string */ 383 "Virtual Machine", /* family name string */ 384 NULL 385 }; 386 387 struct smbios_table_type2 smbios_type2_template = { 388 { SMBIOS_TYPE_BOARD, sizeof (struct smbios_table_type2), 0 }, 389 1, /* manufacturer string */ 390 2, /* product string */ 391 3, /* version string */ 392 4, /* serial number string */ 393 5, /* asset tag string */ 394 SMBIOS_BRF_HOSTING, /* feature flags */ 395 6, /* location string */ 396 SMBIOS_CHT_DESKTOP, /* chassis handle */ 397 SMBIOS_BRT_MOTHERBOARD, /* board type */ 398 0 399 }; 400 401 const char *smbios_type2_strings[] = { 402 "illumos", /* manufacturer string */ 403 "BHYVE", /* product name string */ 404 "1.0", /* version string */ 405 "None", /* serial number string */ 406 "None", /* asset tag string */ 407 "None", /* location string */ 408 NULL 409 }; 410 411 struct smbios_table_type3 smbios_type3_template = { 412 { SMBIOS_TYPE_CHASSIS, sizeof (struct smbios_table_type3), 0 }, 413 1, /* manufacturer string */ 414 SMBIOS_CHT_UNKNOWN, 415 2, /* version string */ 416 3, /* serial number string */ 417 4, /* asset tag string */ 418 SMBIOS_CHST_SAFE, 419 SMBIOS_CHST_SAFE, 420 SMBIOS_CHST_SAFE, 421 SMBIOS_CHSC_NONE, 422 0, /* OEM specific data, we have none */ 423 0, /* height in 'u's (0=enclosure height unspecified) */ 424 0, /* number of power cords (0=number unspecified) */ 425 0, /* number of contained element records */ 426 0, /* length of records */ 427 5 /* sku number string */ 428 }; 429 430 const char *smbios_type3_strings[] = { 431 "illumos", /* manufacturer string */ 432 "1.0", /* version string */ 433 "None", /* serial number string */ 434 "None", /* asset tag string */ 435 "None", /* sku number string */ 436 NULL 437 }; 438 439 struct smbios_table_type4 smbios_type4_template = { 440 { SMBIOS_TYPE_PROCESSOR, sizeof (struct smbios_table_type4), 0 }, 441 1, /* socket designation string */ 442 SMBIOS_PRT_CENTRAL, 443 SMBIOS_PRF_OTHER, 444 2, /* manufacturer string */ 445 0, /* cpuid */ 446 3, /* version string */ 447 0, /* voltage */ 448 0, /* external clock frequency in mhz (0=unknown) */ 449 0, /* maximum frequency in mhz (0=unknown) */ 450 0, /* current frequency in mhz (0=unknown) */ 451 SMBIOS_PRS_PRESENT | SMBIOS_PRS_ENABLED, 452 SMBIOS_PRU_NONE, 453 -1, /* l1 cache handle */ 454 -1, /* l2 cache handle */ 455 -1, /* l3 cache handle */ 456 4, /* serial number string */ 457 5, /* asset tag string */ 458 6, /* part number string */ 459 0, /* cores per socket (0=unknown) */ 460 0, /* enabled cores per socket (0=unknown) */ 461 0, /* threads per socket (0=unknown) */ 462 SMBIOS_PFL_64B, 463 SMBIOS_PRF_OTHER 464 }; 465 466 const char *smbios_type4_strings[] = { 467 " ", /* socket designation string */ 468 " ", /* manufacturer string */ 469 " ", /* version string */ 470 "None", /* serial number string */ 471 "None", /* asset tag string */ 472 "None", /* part number string */ 473 NULL 474 }; 475 476 static int smbios_type4_initializer(struct smbios_structure *template_entry, 477 const char **template_strings, char *curaddr, char **endaddr, 478 uint16_t *n, uint16_t *size); 479 480 struct smbios_table_type16 smbios_type16_template = { 481 { SMBIOS_TYPE_MEMARRAY, sizeof (struct smbios_table_type16), 0 }, 482 SMBIOS_MAL_SYSMB, 483 SMBIOS_MAU_SYSTEM, 484 SMBIOS_MAE_NONE, 485 0x80000000, /* max mem capacity in kb (0x80000000=use extended) */ 486 -1, /* handle of error (if any) */ 487 0, /* number of slots or sockets (TBD) */ 488 0 /* extended maximum memory capacity in bytes (TBD) */ 489 }; 490 491 static int smbios_type16_initializer(struct smbios_structure *template_entry, 492 const char **template_strings, char *curaddr, char **endaddr, 493 uint16_t *n, uint16_t *size); 494 495 struct smbios_table_type17 smbios_type17_template = { 496 { SMBIOS_TYPE_MEMDEVICE, sizeof (struct smbios_table_type17), 0 }, 497 -1, /* handle of physical memory array */ 498 -1, /* handle of memory error data */ 499 64, /* total width in bits including ecc */ 500 64, /* data width in bits */ 501 0, /* size in kb or mb (0x7fff=use extended)*/ 502 SMBIOS_MDFF_UNKNOWN, 503 0, /* set (0x00=none, 0xff=unknown) */ 504 1, /* device locator string */ 505 2, /* physical bank locator string */ 506 SMBIOS_MDT_UNKNOWN, 507 SMBIOS_MDF_UNKNOWN, 508 0, /* maximum memory speed in mhz (0=unknown) */ 509 3, /* manufacturer string */ 510 4, /* serial number string */ 511 5, /* asset tag string */ 512 6, /* part number string */ 513 0, /* attributes (0=unknown rank information) */ 514 0, /* extended size in mb (TBD) */ 515 0, /* current speed in mhz (0=unknown) */ 516 0, /* minimum voltage in mv (0=unknown) */ 517 0, /* maximum voltage in mv (0=unknown) */ 518 0 /* configured voltage in mv (0=unknown) */ 519 }; 520 521 const char *smbios_type17_strings[] = { 522 " ", /* device locator string */ 523 " ", /* physical bank locator string */ 524 " ", /* manufacturer string */ 525 "None", /* serial number string */ 526 "None", /* asset tag string */ 527 "None", /* part number string */ 528 NULL 529 }; 530 531 static int smbios_type17_initializer(struct smbios_structure *template_entry, 532 const char **template_strings, char *curaddr, char **endaddr, 533 uint16_t *n, uint16_t *size); 534 535 struct smbios_table_type19 smbios_type19_template = { 536 { SMBIOS_TYPE_MEMARRAYMAP, sizeof (struct smbios_table_type19), 0 }, 537 0xffffffff, /* starting phys addr in kb (0xffffffff=use ext) */ 538 0xffffffff, /* ending phys addr in kb (0xffffffff=use ext) */ 539 -1, /* physical memory array handle */ 540 1, /* number of devices that form a row */ 541 0, /* extended starting phys addr in bytes (TDB) */ 542 0 /* extended ending phys addr in bytes (TDB) */ 543 }; 544 545 static int smbios_type19_initializer(struct smbios_structure *template_entry, 546 const char **template_strings, char *curaddr, char **endaddr, 547 uint16_t *n, uint16_t *size); 548 549 struct smbios_table_type32 smbios_type32_template = { 550 { SMBIOS_TYPE_BOOT, sizeof (struct smbios_table_type32), 0 }, 551 { 0, 0, 0, 0, 0, 0 }, 552 SMBIOS_BOOT_NORMAL 553 }; 554 555 struct smbios_table_type127 smbios_type127_template = { 556 { SMBIOS_TYPE_EOT, sizeof (struct smbios_table_type127), 0 } 557 }; 558 559 static int smbios_generic_initializer(struct smbios_structure *template_entry, 560 const char **template_strings, char *curaddr, char **endaddr, 561 uint16_t *n, uint16_t *size); 562 563 static struct smbios_template_entry smbios_template[] = { 564 { (struct smbios_structure *)&smbios_type0_template, 565 smbios_type0_strings, 566 smbios_generic_initializer }, 567 { (struct smbios_structure *)&smbios_type1_template, 568 smbios_type1_strings, 569 smbios_type1_initializer }, 570 { (struct smbios_structure *)&smbios_type2_template, 571 smbios_type2_strings, 572 smbios_generic_initializer }, 573 { (struct smbios_structure *)&smbios_type3_template, 574 smbios_type3_strings, 575 smbios_generic_initializer }, 576 { (struct smbios_structure *)&smbios_type4_template, 577 smbios_type4_strings, 578 smbios_type4_initializer }, 579 { (struct smbios_structure *)&smbios_type16_template, 580 NULL, 581 smbios_type16_initializer }, 582 { (struct smbios_structure *)&smbios_type17_template, 583 smbios_type17_strings, 584 smbios_type17_initializer }, 585 { (struct smbios_structure *)&smbios_type19_template, 586 NULL, 587 smbios_type19_initializer }, 588 { (struct smbios_structure *)&smbios_type32_template, 589 NULL, 590 smbios_generic_initializer }, 591 { (struct smbios_structure *)&smbios_type127_template, 592 NULL, 593 smbios_generic_initializer }, 594 { NULL,NULL, NULL } 595 }; 596 597 static uint64_t guest_lomem, guest_himem; 598 static uint16_t type16_handle; 599 600 static int 601 smbios_generic_initializer(struct smbios_structure *template_entry, 602 const char **template_strings, char *curaddr, char **endaddr, 603 uint16_t *n, uint16_t *size) 604 { 605 struct smbios_structure *entry; 606 607 memcpy(curaddr, template_entry, template_entry->length); 608 entry = (struct smbios_structure *)curaddr; 609 entry->handle = *n + 1; 610 curaddr += entry->length; 611 if (template_strings != NULL) { 612 int i; 613 614 for (i = 0; template_strings[i] != NULL; i++) { 615 const char *string; 616 int len; 617 618 string = template_strings[i]; 619 len = strlen(string) + 1; 620 memcpy(curaddr, string, len); 621 curaddr += len; 622 } 623 *curaddr = '\0'; 624 curaddr++; 625 } else { 626 /* Minimum string section is double nul */ 627 *curaddr = '\0'; 628 curaddr++; 629 *curaddr = '\0'; 630 curaddr++; 631 } 632 (*n)++; 633 *endaddr = curaddr; 634 635 return (0); 636 } 637 638 static int 639 smbios_type1_initializer(struct smbios_structure *template_entry, 640 const char **template_strings, char *curaddr, char **endaddr, 641 uint16_t *n, uint16_t *size) 642 { 643 struct smbios_table_type1 *type1; 644 const char *guest_uuid_str; 645 646 smbios_generic_initializer(template_entry, template_strings, 647 curaddr, endaddr, n, size); 648 type1 = (struct smbios_table_type1 *)curaddr; 649 650 guest_uuid_str = get_config_value("uuid"); 651 if (guest_uuid_str != NULL) { 652 uuid_t uuid; 653 uint32_t status; 654 655 uuid_from_string(guest_uuid_str, &uuid, &status); 656 if (status != uuid_s_ok) 657 return (-1); 658 659 uuid_enc_le(&type1->uuid, &uuid); 660 } else { 661 MD5_CTX mdctx; 662 u_char digest[16]; 663 char hostname[MAXHOSTNAMELEN]; 664 const char *vmname; 665 666 /* 667 * Universally unique and yet reproducible are an 668 * oxymoron, however reproducible is desirable in 669 * this case. 670 */ 671 if (gethostname(hostname, sizeof(hostname))) 672 return (-1); 673 674 MD5Init(&mdctx); 675 vmname = get_config_value("name"); 676 MD5Update(&mdctx, vmname, strlen(vmname)); 677 MD5Update(&mdctx, hostname, sizeof(hostname)); 678 MD5Final(digest, &mdctx); 679 680 /* 681 * Set the variant and version number. 682 */ 683 digest[6] &= 0x0F; 684 digest[6] |= 0x30; /* version 3 */ 685 digest[8] &= 0x3F; 686 digest[8] |= 0x80; 687 688 memcpy(&type1->uuid, digest, sizeof (digest)); 689 } 690 691 return (0); 692 } 693 694 static int 695 smbios_type4_initializer(struct smbios_structure *template_entry, 696 const char **template_strings, char *curaddr, char **endaddr, 697 uint16_t *n, uint16_t *size) 698 { 699 int i; 700 701 for (i = 0; i < sockets; i++) { 702 struct smbios_table_type4 *type4; 703 char *p; 704 int nstrings, len; 705 706 smbios_generic_initializer(template_entry, template_strings, 707 curaddr, endaddr, n, size); 708 type4 = (struct smbios_table_type4 *)curaddr; 709 p = curaddr + sizeof (struct smbios_table_type4); 710 nstrings = 0; 711 while (p < *endaddr - 1) { 712 if (*p++ == '\0') 713 nstrings++; 714 } 715 len = sprintf(*endaddr - 1, "CPU #%d", i) + 1; 716 *endaddr += len - 1; 717 *(*endaddr) = '\0'; 718 (*endaddr)++; 719 type4->socket = nstrings + 1; 720 /* Revise cores and threads after update to smbios 3.0 */ 721 if (cores > 254) 722 type4->cores = 0; 723 else 724 type4->cores = cores; 725 /* This threads is total threads in a socket */ 726 if ((cores * threads) > 254) 727 type4->threads = 0; 728 else 729 type4->threads = (cores * threads); 730 curaddr = *endaddr; 731 } 732 733 return (0); 734 } 735 736 static int 737 smbios_type16_initializer(struct smbios_structure *template_entry, 738 const char **template_strings, char *curaddr, char **endaddr, 739 uint16_t *n, uint16_t *size) 740 { 741 struct smbios_table_type16 *type16; 742 743 type16_handle = *n; 744 smbios_generic_initializer(template_entry, template_strings, 745 curaddr, endaddr, n, size); 746 type16 = (struct smbios_table_type16 *)curaddr; 747 type16->xsize = guest_lomem + guest_himem; 748 type16->ndevs = guest_himem > 0 ? 2 : 1; 749 750 return (0); 751 } 752 753 static int 754 smbios_type17_initializer(struct smbios_structure *template_entry, 755 const char **template_strings, char *curaddr, char **endaddr, 756 uint16_t *n, uint16_t *size) 757 { 758 struct smbios_table_type17 *type17; 759 uint64_t memsize, size_KB, size_MB; 760 761 smbios_generic_initializer(template_entry, template_strings, 762 curaddr, endaddr, n, size); 763 type17 = (struct smbios_table_type17 *)curaddr; 764 type17->arrayhand = type16_handle; 765 766 memsize = guest_lomem + guest_himem; 767 size_KB = memsize / 1024; 768 size_MB = memsize / MB; 769 770 /* A single Type 17 entry can't represent more than ~2PB RAM */ 771 if (size_MB > 0x7FFFFFFF) { 772 printf("Warning: guest memory too big for SMBIOS Type 17 table: " 773 "%luMB greater than max supported 2147483647MB\n", size_MB); 774 775 size_MB = 0x7FFFFFFF; 776 } 777 778 /* See SMBIOS 2.7.0 section 7.18 - Memory Device (Type 17) */ 779 if (size_KB <= 0x7FFF) { 780 /* Can represent up to 32767KB with the top bit set */ 781 type17->size = size_KB | (1 << 15); 782 } else if (size_MB < 0x7FFF) { 783 /* Can represent up to 32766MB with the top bit unset */ 784 type17->size = size_MB & 0x7FFF; 785 } else { 786 type17->size = 0x7FFF; 787 /* 788 * Can represent up to 2147483647MB (~2PB) 789 * The top bit is reserved 790 */ 791 type17->xsize = size_MB & 0x7FFFFFFF; 792 } 793 794 return (0); 795 } 796 797 static int 798 smbios_type19_initializer(struct smbios_structure *template_entry, 799 const char **template_strings, char *curaddr, char **endaddr, 800 uint16_t *n, uint16_t *size) 801 { 802 struct smbios_table_type19 *type19; 803 804 smbios_generic_initializer(template_entry, template_strings, 805 curaddr, endaddr, n, size); 806 type19 = (struct smbios_table_type19 *)curaddr; 807 type19->arrayhand = type16_handle; 808 type19->xsaddr = 0; 809 type19->xeaddr = guest_lomem; 810 811 if (guest_himem > 0) { 812 curaddr = *endaddr; 813 smbios_generic_initializer(template_entry, template_strings, 814 curaddr, endaddr, n, size); 815 type19 = (struct smbios_table_type19 *)curaddr; 816 type19->arrayhand = type16_handle; 817 type19->xsaddr = 4*GB; 818 type19->xeaddr = type19->xsaddr + guest_himem; 819 } 820 821 return (0); 822 } 823 824 static void 825 smbios_ep_initializer(struct smbios_entry_point *smbios_ep, uint32_t staddr) 826 { 827 memset(smbios_ep, 0, sizeof(*smbios_ep)); 828 memcpy(smbios_ep->eanchor, SMBIOS_ENTRY_EANCHOR, 829 SMBIOS_ENTRY_EANCHORLEN); 830 smbios_ep->eplen = 0x1F; 831 assert(sizeof (struct smbios_entry_point) == smbios_ep->eplen); 832 smbios_ep->major = 2; 833 smbios_ep->minor = 6; 834 smbios_ep->revision = 0; 835 memcpy(smbios_ep->ianchor, SMBIOS_ENTRY_IANCHOR, 836 SMBIOS_ENTRY_IANCHORLEN); 837 smbios_ep->staddr = staddr; 838 smbios_ep->bcdrev = (smbios_ep->major & 0xf) << 4 | (smbios_ep->minor & 0xf); 839 } 840 841 static void 842 smbios_ep_finalizer(struct smbios_entry_point *smbios_ep, uint16_t len, 843 uint16_t num, uint16_t maxssize) 844 { 845 uint8_t checksum; 846 int i; 847 848 smbios_ep->maxssize = maxssize; 849 smbios_ep->stlen = len; 850 smbios_ep->stnum = num; 851 852 checksum = 0; 853 for (i = 0x10; i < 0x1f; i++) { 854 checksum -= ((uint8_t *)smbios_ep)[i]; 855 } 856 smbios_ep->ichecksum = checksum; 857 858 checksum = 0; 859 for (i = 0; i < 0x1f; i++) { 860 checksum -= ((uint8_t *)smbios_ep)[i]; 861 } 862 smbios_ep->echecksum = checksum; 863 } 864 865 int 866 smbios_build(struct vmctx *ctx) 867 { 868 struct smbios_entry_point *smbios_ep; 869 uint16_t n; 870 uint16_t maxssize; 871 char *curaddr, *startaddr, *ststartaddr; 872 int i; 873 int err; 874 875 guest_lomem = vm_get_lowmem_size(ctx); 876 guest_himem = vm_get_highmem_size(ctx); 877 878 startaddr = paddr_guest2host(ctx, SMBIOS_BASE, SMBIOS_MAX_LENGTH); 879 if (startaddr == NULL) { 880 EPRINTLN("smbios table requires mapped mem"); 881 return (ENOMEM); 882 } 883 884 curaddr = startaddr; 885 886 smbios_ep = (struct smbios_entry_point *)curaddr; 887 smbios_ep_initializer(smbios_ep, SMBIOS_BASE + 888 sizeof(struct smbios_entry_point)); 889 curaddr += sizeof(struct smbios_entry_point); 890 ststartaddr = curaddr; 891 892 n = 0; 893 maxssize = 0; 894 for (i = 0; smbios_template[i].entry != NULL; i++) { 895 struct smbios_structure *entry; 896 const char **strings; 897 initializer_func_t initializer; 898 char *endaddr; 899 uint16_t size; 900 901 entry = smbios_template[i].entry; 902 strings = smbios_template[i].strings; 903 initializer = smbios_template[i].initializer; 904 905 err = (*initializer)(entry, strings, curaddr, &endaddr, 906 &n, &size); 907 if (err != 0) 908 return (err); 909 910 if (size > maxssize) 911 maxssize = size; 912 913 curaddr = endaddr; 914 } 915 916 assert(curaddr - startaddr < SMBIOS_MAX_LENGTH); 917 smbios_ep_finalizer(smbios_ep, curaddr - ststartaddr, n, maxssize); 918 919 return (0); 920 } 921 922 #ifndef __FreeBSD__ 923 struct { 924 const char *key; 925 const char **targetp; 926 } type1_map[] = { 927 { "manufacturer", &smbios_type1_strings[0] }, 928 { "product", &smbios_type1_strings[1] }, 929 { "version", &smbios_type1_strings[2] }, 930 { "serial", &smbios_type1_strings[3] }, 931 { "sku", &smbios_type1_strings[4] }, 932 { "family", &smbios_type1_strings[5] }, 933 { 0 } 934 }; 935 936 void 937 smbios_apply(void) 938 { 939 nvlist_t *nvl; 940 941 nvl = find_config_node("smbios"); 942 if (nvl == NULL) 943 return; 944 945 for (uint_t i = 0; type1_map[i].key != NULL; i++) { 946 const char *value; 947 948 value = get_config_value_node(nvl, type1_map[i].key); 949 if (value != NULL) 950 *type1_map[i].targetp = value; 951 } 952 } 953 954 int 955 smbios_parse(const char *opts) 956 { 957 char *buf, *lasts, *token, *end; 958 nvlist_t *nvl; 959 long type; 960 961 if ((buf = strdup(opts)) == NULL) { 962 (void) fprintf(stderr, "out of memory\n"); 963 return (-1); 964 } 965 966 if ((token = strtok_r(buf, ",", &lasts)) == NULL) { 967 (void) fprintf(stderr, "too few fields\n"); 968 goto fail; 969 } 970 971 errno = 0; 972 type = strtol(token, &end, 10); 973 if (errno != 0 || *end != '\0') { 974 (void) fprintf(stderr, "first token '%s' is not an integer\n", 975 token); 976 goto fail; 977 } 978 979 /* For now, only type 1 is supported. */ 980 if (type != 1) { 981 (void) fprintf(stderr, "unsupported type %d\n", type); 982 goto fail; 983 } 984 985 nvl = create_config_node("smbios"); 986 if (nvl == NULL) { 987 (void) fprintf(stderr, "out of memory\n"); 988 return (-1); 989 } 990 991 while ((token = strtok_r(NULL, ",", &lasts)) != NULL) { 992 char *val; 993 uint_t i; 994 995 if ((val = strchr(token, '=')) == NULL) { 996 (void) fprintf(stderr, "invalid key=value: '%s'\n", 997 token); 998 goto fail; 999 } 1000 *val = '\0'; 1001 val++; 1002 1003 if (strcmp(token, "uuid") == 0) { 1004 set_config_value_node(nvl, token, val); 1005 continue; 1006 } 1007 1008 for (i = 0; type1_map[i].key != NULL; i++) { 1009 if (strcmp(token, type1_map[i].key) == 0) { 1010 break; 1011 } 1012 } 1013 if (type1_map[i].key == NULL) { 1014 (void) fprintf(stderr, "invalid key '%s'\n", token); 1015 goto fail; 1016 } 1017 set_config_value_node(nvl, token, val); 1018 } 1019 1020 return (0); 1021 1022 fail: 1023 free(buf); 1024 return (-1); 1025 } 1026 #endif 1027