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