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