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