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