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