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