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