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