15325 bhyve upstream sync 2023 January
[illumos-gate.git] / usr / src / cmd / bhyve / smbiostbl.c
blobfc2d8060ecb84bb71a35b1ef64bb3da22dc7d534
1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5 * All rights reserved.
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.
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.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
34 #include <assert.h>
35 #include <errno.h>
36 #include <md5.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <uuid.h>
43 #include <machine/vmm.h>
44 #include <vmmapi.h>
46 #ifndef __FreeBSD__
47 #include <sys/sysmacros.h>
48 #endif
50 #include "bhyverun.h"
51 #include "config.h"
52 #include "debug.h"
53 #include "smbiostbl.h"
55 #define MB (1024*1024)
56 #define GB (1024ULL*1024*1024)
58 #define SMBIOS_BASE 0xF1000
60 #define FIRMWARE_VERSION "14.0"
61 /* The SMBIOS specification defines the date format to be mm/dd/yyyy */
62 #define FIRMWARE_RELEASE_DATE "10/10/2021"
64 /* BHYVE_ACPI_BASE - SMBIOS_BASE) */
65 #define SMBIOS_MAX_LENGTH (0xF2400 - 0xF1000)
67 #define SMBIOS_TYPE_BIOS 0
68 #define SMBIOS_TYPE_SYSTEM 1
69 #define SMBIOS_TYPE_BOARD 2
70 #define SMBIOS_TYPE_CHASSIS 3
71 #define SMBIOS_TYPE_PROCESSOR 4
72 #define SMBIOS_TYPE_MEMARRAY 16
73 #define SMBIOS_TYPE_MEMDEVICE 17
74 #define SMBIOS_TYPE_MEMARRAYMAP 19
75 #define SMBIOS_TYPE_BOOT 32
76 #define SMBIOS_TYPE_EOT 127
78 struct smbios_structure {
79 uint8_t type;
80 uint8_t length;
81 uint16_t handle;
82 } __packed;
84 struct smbios_string {
85 const char *node;
86 const char *value;
89 typedef int (*initializer_func_t)(const struct smbios_structure *template_entry,
90 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
91 uint16_t *n);
93 struct smbios_template_entry {
94 const struct smbios_structure *entry;
95 const struct smbios_string *strings;
96 initializer_func_t initializer;
100 * SMBIOS Structure Table Entry Point
102 #define SMBIOS_ENTRY_EANCHOR "_SM_"
103 #define SMBIOS_ENTRY_EANCHORLEN 4
104 #define SMBIOS_ENTRY_IANCHOR "_DMI_"
105 #define SMBIOS_ENTRY_IANCHORLEN 5
107 struct smbios_entry_point {
108 char eanchor[4]; /* anchor tag */
109 uint8_t echecksum; /* checksum of entry point structure */
110 uint8_t eplen; /* length in bytes of entry point */
111 uint8_t major; /* major version of the SMBIOS spec */
112 uint8_t minor; /* minor version of the SMBIOS spec */
113 uint16_t maxssize; /* maximum size in bytes of a struct */
114 uint8_t revision; /* entry point structure revision */
115 uint8_t format[5]; /* entry point rev-specific data */
116 char ianchor[5]; /* intermediate anchor tag */
117 uint8_t ichecksum; /* intermediate checksum */
118 uint16_t stlen; /* len in bytes of structure table */
119 uint32_t staddr; /* physical addr of structure table */
120 uint16_t stnum; /* number of structure table entries */
121 uint8_t bcdrev; /* BCD value representing DMI ver */
122 } __packed;
125 * BIOS Information
127 #define SMBIOS_FL_ISA 0x00000010 /* ISA is supported */
128 #define SMBIOS_FL_PCI 0x00000080 /* PCI is supported */
129 #define SMBIOS_FL_SHADOW 0x00001000 /* BIOS shadowing is allowed */
130 #define SMBIOS_FL_CDBOOT 0x00008000 /* Boot from CD is supported */
131 #define SMBIOS_FL_SELBOOT 0x00010000 /* Selectable Boot supported */
132 #define SMBIOS_FL_EDD 0x00080000 /* EDD Spec is supported */
134 #define SMBIOS_XB1_FL_ACPI 0x00000001 /* ACPI is supported */
136 #define SMBIOS_XB2_FL_BBS 0x00000001 /* BIOS Boot Specification */
137 #define SMBIOS_XB2_FL_VM 0x00000010 /* Virtual Machine */
139 struct smbios_table_type0 {
140 struct smbios_structure header;
141 uint8_t vendor; /* vendor string */
142 uint8_t version; /* version string */
143 uint16_t segment; /* address segment location */
144 uint8_t rel_date; /* release date */
145 uint8_t size; /* rom size */
146 uint64_t cflags; /* characteristics */
147 uint8_t xc_bytes[2]; /* characteristics ext bytes */
148 uint8_t sb_major_rel; /* system bios version */
149 uint8_t sb_minor_rele;
150 uint8_t ecfw_major_rel; /* embedded ctrl fw version */
151 uint8_t ecfw_minor_rel;
152 } __packed;
155 * System Information
157 #define SMBIOS_WAKEUP_SWITCH 0x06 /* power switch */
159 struct smbios_table_type1 {
160 struct smbios_structure header;
161 uint8_t manufacturer; /* manufacturer string */
162 uint8_t product; /* product name string */
163 uint8_t version; /* version string */
164 uint8_t serial; /* serial number string */
165 uint8_t uuid[16]; /* uuid byte array */
166 uint8_t wakeup; /* wake-up event */
167 uint8_t sku; /* sku number string */
168 uint8_t family; /* family name string */
169 } __packed;
172 * Baseboard (or Module) Information
174 #define SMBIOS_BRF_HOSTING 0x1
175 #define SMBIOS_BRT_MOTHERBOARD 0xa
177 struct smbios_table_type2 {
178 struct smbios_structure header;
179 uint8_t manufacturer; /* manufacturer string */
180 uint8_t product; /* product name string */
181 uint8_t version; /* version string */
182 uint8_t serial; /* serial number string */
183 uint8_t asset; /* asset tag string */
184 uint8_t fflags; /* feature flags */
185 uint8_t location; /* location in chassis */
186 uint16_t chandle; /* chassis handle */
187 uint8_t type; /* board type */
188 uint8_t n_objs; /* number of contained object handles */
189 } __packed;
192 * System Enclosure or Chassis
194 #define SMBIOS_CHT_UNKNOWN 0x02 /* unknown */
195 #define SMBIOS_CHT_DESKTOP 0x03 /* desktop */
197 #define SMBIOS_CHST_SAFE 0x03 /* safe */
199 #define SMBIOS_CHSC_NONE 0x03 /* none */
201 struct smbios_table_type3 {
202 struct smbios_structure header;
203 uint8_t manufacturer; /* manufacturer string */
204 uint8_t type; /* type */
205 uint8_t version; /* version string */
206 uint8_t serial; /* serial number string */
207 uint8_t asset; /* asset tag string */
208 uint8_t bustate; /* boot-up state */
209 uint8_t psstate; /* power supply state */
210 uint8_t tstate; /* thermal state */
211 uint8_t security; /* security status */
212 uint32_t oemdata; /* OEM-specific data */
213 uint8_t uheight; /* height in 'u's */
214 uint8_t cords; /* number of power cords */
215 uint8_t elems; /* number of element records */
216 uint8_t elemlen; /* length of records */
217 uint8_t sku; /* sku number string */
218 } __packed;
221 * Processor Information
223 #define SMBIOS_PRT_CENTRAL 0x03 /* central processor */
225 #define SMBIOS_PRF_OTHER 0x01 /* other */
227 #define SMBIOS_PRS_PRESENT 0x40 /* socket is populated */
228 #define SMBIOS_PRS_ENABLED 0x1 /* enabled */
230 #define SMBIOS_PRU_NONE 0x06 /* none */
232 #define SMBIOS_PFL_64B 0x04 /* 64-bit capable */
234 struct smbios_table_type4 {
235 struct smbios_structure header;
236 uint8_t socket; /* socket designation string */
237 uint8_t type; /* processor type */
238 uint8_t family; /* processor family */
239 uint8_t manufacturer; /* manufacturer string */
240 uint64_t cpuid; /* processor cpuid */
241 uint8_t version; /* version string */
242 uint8_t voltage; /* voltage */
243 uint16_t clkspeed; /* ext clock speed in mhz */
244 uint16_t maxspeed; /* maximum speed in mhz */
245 uint16_t curspeed; /* current speed in mhz */
246 uint8_t status; /* status */
247 uint8_t upgrade; /* upgrade */
248 uint16_t l1handle; /* l1 cache handle */
249 uint16_t l2handle; /* l2 cache handle */
250 uint16_t l3handle; /* l3 cache handle */
251 uint8_t serial; /* serial number string */
252 uint8_t asset; /* asset tag string */
253 uint8_t part; /* part number string */
254 uint8_t cores; /* cores per socket */
255 uint8_t ecores; /* enabled cores */
256 uint8_t threads; /* threads per socket */
257 uint16_t cflags; /* processor characteristics */
258 uint16_t family2; /* processor family 2 */
259 } __packed;
262 * Physical Memory Array
264 #define SMBIOS_MAL_SYSMB 0x03 /* system board or motherboard */
266 #define SMBIOS_MAU_SYSTEM 0x03 /* system memory */
268 #define SMBIOS_MAE_NONE 0x03 /* none */
270 struct smbios_table_type16 {
271 struct smbios_structure header;
272 uint8_t location; /* physical device location */
273 uint8_t use; /* device functional purpose */
274 uint8_t ecc; /* err detect/correct method */
275 uint32_t size; /* max mem capacity in kb */
276 uint16_t errhand; /* handle of error (if any) */
277 uint16_t ndevs; /* num of slots or sockets */
278 uint64_t xsize; /* max mem capacity in bytes */
279 } __packed;
282 * Memory Device
284 #define SMBIOS_MDFF_UNKNOWN 0x02 /* unknown */
286 #define SMBIOS_MDT_UNKNOWN 0x02 /* unknown */
288 #define SMBIOS_MDF_UNKNOWN 0x0004 /* unknown */
290 struct smbios_table_type17 {
291 struct smbios_structure header;
292 uint16_t arrayhand; /* handle of physl mem array */
293 uint16_t errhand; /* handle of mem error data */
294 uint16_t twidth; /* total width in bits */
295 uint16_t dwidth; /* data width in bits */
296 uint16_t size; /* size in kb or mb */
297 uint8_t form; /* form factor */
298 uint8_t set; /* set */
299 uint8_t dloc; /* device locator string */
300 uint8_t bloc; /* phys bank locator string */
301 uint8_t type; /* memory type */
302 uint16_t flags; /* memory characteristics */
303 uint16_t maxspeed; /* maximum speed in mhz */
304 uint8_t manufacturer; /* manufacturer string */
305 uint8_t serial; /* serial number string */
306 uint8_t asset; /* asset tag string */
307 uint8_t part; /* part number string */
308 uint8_t attributes; /* attributes */
309 uint32_t xsize; /* extended size in mb */
310 uint16_t curspeed; /* current speed in mhz */
311 uint16_t minvoltage; /* minimum voltage */
312 uint16_t maxvoltage; /* maximum voltage */
313 uint16_t curvoltage; /* configured voltage */
314 } __packed;
317 * Memory Array Mapped Address
319 struct smbios_table_type19 {
320 struct smbios_structure header;
321 uint32_t saddr; /* start phys addr in kb */
322 uint32_t eaddr; /* end phys addr in kb */
323 uint16_t arrayhand; /* physical mem array handle */
324 uint8_t width; /* num of dev in row */
325 uint64_t xsaddr; /* start phys addr in bytes */
326 uint64_t xeaddr; /* end phys addr in bytes */
327 } __packed;
330 * System Boot Information
332 #define SMBIOS_BOOT_NORMAL 0 /* no errors detected */
334 struct smbios_table_type32 {
335 struct smbios_structure header;
336 uint8_t reserved[6];
337 uint8_t status; /* boot status */
338 } __packed;
341 * End-of-Table
343 struct smbios_table_type127 {
344 struct smbios_structure header;
345 } __packed;
347 static const struct smbios_table_type0 smbios_type0_template = {
348 { SMBIOS_TYPE_BIOS, sizeof (struct smbios_table_type0), 0 },
349 1, /* bios vendor string */
350 2, /* bios version string */
351 0xF000, /* bios address segment location */
352 3, /* bios release date */
353 0x0, /* bios size (64k * (n + 1) is the size in bytes) */
354 SMBIOS_FL_ISA | SMBIOS_FL_PCI | SMBIOS_FL_SHADOW |
355 SMBIOS_FL_CDBOOT | SMBIOS_FL_EDD,
356 { SMBIOS_XB1_FL_ACPI, SMBIOS_XB2_FL_BBS | SMBIOS_XB2_FL_VM },
357 0x0, /* bios major release */
358 0x0, /* bios minor release */
359 0xff, /* embedded controller firmware major release */
360 0xff /* embedded controller firmware minor release */
363 static const struct smbios_string smbios_type0_strings[] = {
364 { "bios.vendor", "BHYVE" }, /* vendor string */
365 { "bios.version", FIRMWARE_VERSION }, /* bios version string */
366 { "bios.release_date", FIRMWARE_RELEASE_DATE }, /* bios release date string */
367 { 0 }
370 static const struct smbios_table_type1 smbios_type1_template = {
371 { SMBIOS_TYPE_SYSTEM, sizeof (struct smbios_table_type1), 0 },
372 1, /* manufacturer string */
373 2, /* product string */
374 3, /* version string */
375 4, /* serial number string */
376 { 0 },
377 SMBIOS_WAKEUP_SWITCH,
378 5, /* sku string */
379 6 /* family string */
382 static int smbios_type1_initializer(const struct smbios_structure *template_entry,
383 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
384 uint16_t *n);
386 static const struct smbios_string smbios_type1_strings[] = {
387 { "system.manufacturer", "illumos" }, /* manufacturer string */
388 { "system.product_name", "BHYVE" }, /* product string */
389 { "system.version", "1.0" }, /* version string */
390 { "system.serial_number", "None" }, /* serial number string */
391 { "system.sku", "None" }, /* sku string */
392 { "system.family_name", "Virtual Machine" }, /* family string */
393 { 0 }
396 static const struct smbios_table_type2 smbios_type2_template = {
397 { SMBIOS_TYPE_BOARD, sizeof (struct smbios_table_type2), 0 },
398 1, /* manufacturer string */
399 2, /* product string */
400 3, /* version string */
401 4, /* serial number string */
402 5, /* asset tag string */
403 SMBIOS_BRF_HOSTING, /* feature flags */
404 6, /* location string */
405 SMBIOS_CHT_DESKTOP, /* chassis handle */
406 SMBIOS_BRT_MOTHERBOARD, /* board type */
410 static const struct smbios_string smbios_type2_strings[] = {
411 { "board.manufacturer", "illumos" }, /* manufacturer string */
412 { "board.product_name", "BHYVE" }, /* product name string */
413 { "board.version", "1.0" }, /* version string */
414 { "board.serial_number", "None" }, /* serial number string */
415 { "board.asset_tag", "None" }, /* asset tag string */
416 { "board.location", "None" }, /* location string */
417 { 0 }
420 static const struct smbios_table_type3 smbios_type3_template = {
421 { SMBIOS_TYPE_CHASSIS, sizeof (struct smbios_table_type3), 0 },
422 1, /* manufacturer string */
423 SMBIOS_CHT_UNKNOWN,
424 2, /* version string */
425 3, /* serial number string */
426 4, /* asset tag string */
427 SMBIOS_CHST_SAFE,
428 SMBIOS_CHST_SAFE,
429 SMBIOS_CHST_SAFE,
430 SMBIOS_CHSC_NONE,
431 0, /* OEM specific data, we have none */
432 0, /* height in 'u's (0=enclosure height unspecified) */
433 0, /* number of power cords (0=number unspecified) */
434 0, /* number of contained element records */
435 0, /* length of records */
436 5 /* sku number string */
439 static const struct smbios_string smbios_type3_strings[] = {
440 { "chassis.manufacturer", "illumos" }, /* manufacturer string */
441 { "chassis.version", "1.0" }, /* version string */
442 { "chassis.serial_number", "None" }, /* serial number string */
443 { "chassis.asset_tag", "None" }, /* asset tag string */
444 { "chassis.sku", "None" }, /* sku number string */
445 { 0 }
448 static const struct smbios_table_type4 smbios_type4_template = {
449 { SMBIOS_TYPE_PROCESSOR, sizeof (struct smbios_table_type4), 0 },
450 1, /* socket designation string */
451 SMBIOS_PRT_CENTRAL,
452 SMBIOS_PRF_OTHER,
453 2, /* manufacturer string */
454 0, /* cpuid */
455 3, /* version string */
456 0, /* voltage */
457 0, /* external clock frequency in mhz (0=unknown) */
458 0, /* maximum frequency in mhz (0=unknown) */
459 0, /* current frequency in mhz (0=unknown) */
460 SMBIOS_PRS_PRESENT | SMBIOS_PRS_ENABLED,
461 SMBIOS_PRU_NONE,
462 -1, /* l1 cache handle */
463 -1, /* l2 cache handle */
464 -1, /* l3 cache handle */
465 4, /* serial number string */
466 5, /* asset tag string */
467 6, /* part number string */
468 0, /* cores per socket (0=unknown) */
469 0, /* enabled cores per socket (0=unknown) */
470 0, /* threads per socket (0=unknown) */
471 SMBIOS_PFL_64B,
472 SMBIOS_PRF_OTHER
475 static const struct smbios_string smbios_type4_strings[] = {
476 { NULL, " " }, /* socket designation string */
477 { NULL, " " }, /* manufacturer string */
478 { NULL, " " }, /* version string */
479 { NULL, "None" }, /* serial number string */
480 { NULL, "None" }, /* asset tag string */
481 { NULL, "None" }, /* part number string */
482 { 0 }
485 static int smbios_type4_initializer(
486 const struct smbios_structure *template_entry,
487 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
488 uint16_t *n);
490 static const struct smbios_table_type16 smbios_type16_template = {
491 { SMBIOS_TYPE_MEMARRAY, sizeof (struct smbios_table_type16), 0 },
492 SMBIOS_MAL_SYSMB,
493 SMBIOS_MAU_SYSTEM,
494 SMBIOS_MAE_NONE,
495 0x80000000, /* max mem capacity in kb (0x80000000=use extended) */
496 -1, /* handle of error (if any) */
497 0, /* number of slots or sockets (TBD) */
498 0 /* extended maximum memory capacity in bytes (TBD) */
501 static int smbios_type16_initializer(
502 const struct smbios_structure *template_entry,
503 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
504 uint16_t *n);
506 static const struct smbios_table_type17 smbios_type17_template = {
507 { SMBIOS_TYPE_MEMDEVICE, sizeof (struct smbios_table_type17), 0 },
508 -1, /* handle of physical memory array */
509 -1, /* handle of memory error data */
510 64, /* total width in bits including ecc */
511 64, /* data width in bits */
512 0, /* size in kb or mb (0x7fff=use extended)*/
513 SMBIOS_MDFF_UNKNOWN,
514 0, /* set (0x00=none, 0xff=unknown) */
515 1, /* device locator string */
516 2, /* physical bank locator string */
517 SMBIOS_MDT_UNKNOWN,
518 SMBIOS_MDF_UNKNOWN,
519 0, /* maximum memory speed in mhz (0=unknown) */
520 3, /* manufacturer string */
521 4, /* serial number string */
522 5, /* asset tag string */
523 6, /* part number string */
524 0, /* attributes (0=unknown rank information) */
525 0, /* extended size in mb (TBD) */
526 0, /* current speed in mhz (0=unknown) */
527 0, /* minimum voltage in mv (0=unknown) */
528 0, /* maximum voltage in mv (0=unknown) */
529 0 /* configured voltage in mv (0=unknown) */
532 static const struct smbios_string smbios_type17_strings[] = {
533 { NULL, " " }, /* device locator string */
534 { NULL, " " }, /* physical bank locator string */
535 { NULL, " " }, /* manufacturer string */
536 { NULL, "None" }, /* serial number string */
537 { NULL, "None" }, /* asset tag string */
538 { NULL, "None" }, /* part number string */
539 { 0 }
542 static int smbios_type17_initializer(
543 const struct smbios_structure *template_entry,
544 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
545 uint16_t *n);
547 static const struct smbios_table_type19 smbios_type19_template = {
548 { SMBIOS_TYPE_MEMARRAYMAP, sizeof (struct smbios_table_type19), 0 },
549 0xffffffff, /* starting phys addr in kb (0xffffffff=use ext) */
550 0xffffffff, /* ending phys addr in kb (0xffffffff=use ext) */
551 -1, /* physical memory array handle */
552 1, /* number of devices that form a row */
553 0, /* extended starting phys addr in bytes (TDB) */
554 0 /* extended ending phys addr in bytes (TDB) */
557 static int smbios_type19_initializer(
558 const struct smbios_structure *template_entry,
559 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
560 uint16_t *n);
562 static struct smbios_table_type32 smbios_type32_template = {
563 { SMBIOS_TYPE_BOOT, sizeof (struct smbios_table_type32), 0 },
564 { 0, 0, 0, 0, 0, 0 },
565 SMBIOS_BOOT_NORMAL
568 static const struct smbios_table_type127 smbios_type127_template = {
569 { SMBIOS_TYPE_EOT, sizeof (struct smbios_table_type127), 0 }
572 static int smbios_generic_initializer(
573 const struct smbios_structure *template_entry,
574 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
575 uint16_t *n);
577 static struct smbios_template_entry smbios_template[] = {
578 { (const struct smbios_structure *)&smbios_type0_template,
579 smbios_type0_strings,
580 smbios_generic_initializer },
581 { (const struct smbios_structure *)&smbios_type1_template,
582 smbios_type1_strings,
583 smbios_type1_initializer },
584 { (const struct smbios_structure *)&smbios_type2_template,
585 smbios_type2_strings,
586 smbios_generic_initializer },
587 { (const struct smbios_structure *)&smbios_type3_template,
588 smbios_type3_strings,
589 smbios_generic_initializer },
590 { (const struct smbios_structure *)&smbios_type4_template,
591 smbios_type4_strings,
592 smbios_type4_initializer },
593 { (const struct smbios_structure *)&smbios_type16_template,
594 NULL,
595 smbios_type16_initializer },
596 { (const struct smbios_structure *)&smbios_type17_template,
597 smbios_type17_strings,
598 smbios_type17_initializer },
599 { (const struct smbios_structure *)&smbios_type19_template,
600 NULL,
601 smbios_type19_initializer },
602 { (const struct smbios_structure *)&smbios_type32_template,
603 NULL,
604 smbios_generic_initializer },
605 { (const struct smbios_structure *)&smbios_type127_template,
606 NULL,
607 smbios_generic_initializer },
608 { NULL,NULL, NULL }
611 static uint64_t guest_lomem, guest_himem;
612 static uint16_t type16_handle;
614 static int
615 smbios_generic_initializer(const struct smbios_structure *template_entry,
616 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
617 uint16_t *n)
619 struct smbios_structure *entry;
621 memcpy(curaddr, template_entry, template_entry->length);
622 entry = (struct smbios_structure *)curaddr;
623 entry->handle = *n + 1;
624 curaddr += entry->length;
625 if (template_strings != NULL) {
626 int i;
628 for (i = 0; template_strings[i].value != NULL; i++) {
629 const char *string;
630 int len;
632 if (template_strings[i].node == NULL) {
633 string = template_strings[i].value;
634 } else {
635 set_config_value_if_unset(
636 template_strings[i].node,
637 template_strings[i].value);
638 string = get_config_value(
639 template_strings[i].node);
642 len = strlen(string) + 1;
643 memcpy(curaddr, string, len);
644 curaddr += len;
646 *curaddr = '\0';
647 curaddr++;
648 } else {
649 /* Minimum string section is double nul */
650 *curaddr = '\0';
651 curaddr++;
652 *curaddr = '\0';
653 curaddr++;
655 (*n)++;
656 *endaddr = curaddr;
658 return (0);
661 static int
662 smbios_type1_initializer(const struct smbios_structure *template_entry,
663 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
664 uint16_t *n)
666 struct smbios_table_type1 *type1;
667 const char *guest_uuid_str;
669 smbios_generic_initializer(template_entry, template_strings,
670 curaddr, endaddr, n);
671 type1 = (struct smbios_table_type1 *)curaddr;
673 guest_uuid_str = get_config_value("uuid");
674 if (guest_uuid_str != NULL) {
675 uuid_t uuid;
676 uint32_t status;
678 uuid_from_string(guest_uuid_str, &uuid, &status);
679 if (status != uuid_s_ok) {
680 EPRINTLN("Invalid UUID");
681 return (-1);
684 uuid_enc_le(&type1->uuid, &uuid);
685 } else {
686 MD5_CTX mdctx;
687 u_char digest[16];
688 char hostname[MAXHOSTNAMELEN];
689 const char *vmname;
692 * Universally unique and yet reproducible are an
693 * oxymoron, however reproducible is desirable in
694 * this case.
696 if (gethostname(hostname, sizeof(hostname)))
697 return (-1);
699 MD5Init(&mdctx);
700 vmname = get_config_value("name");
701 MD5Update(&mdctx, vmname, strlen(vmname));
702 MD5Update(&mdctx, hostname, sizeof(hostname));
703 MD5Final(digest, &mdctx);
706 * Set the variant and version number.
708 digest[6] &= 0x0F;
709 digest[6] |= 0x30; /* version 3 */
710 digest[8] &= 0x3F;
711 digest[8] |= 0x80;
713 memcpy(&type1->uuid, digest, sizeof (digest));
716 return (0);
719 static int
720 smbios_type4_initializer(const struct smbios_structure *template_entry,
721 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
722 uint16_t *n)
724 int i;
726 for (i = 0; i < cpu_sockets; i++) {
727 struct smbios_table_type4 *type4;
728 char *p;
729 int nstrings, len;
731 smbios_generic_initializer(template_entry, template_strings,
732 curaddr, endaddr, n);
733 type4 = (struct smbios_table_type4 *)curaddr;
734 p = curaddr + sizeof (struct smbios_table_type4);
735 nstrings = 0;
736 while (p < *endaddr - 1) {
737 if (*p++ == '\0')
738 nstrings++;
740 len = sprintf(*endaddr - 1, "CPU #%d", i) + 1;
741 *endaddr += len - 1;
742 *(*endaddr) = '\0';
743 (*endaddr)++;
744 type4->socket = nstrings + 1;
745 /* Revise cores and threads after update to smbios 3.0 */
746 if (cpu_cores > 254)
747 type4->cores = 0;
748 else
749 type4->cores = cpu_cores;
750 /* This threads is total threads in a socket */
751 if (cpu_cores * cpu_threads > 254)
752 type4->threads = 0;
753 else
754 type4->threads = cpu_cores * cpu_threads;
755 curaddr = *endaddr;
758 return (0);
761 static int
762 smbios_type16_initializer(const struct smbios_structure *template_entry,
763 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
764 uint16_t *n)
766 struct smbios_table_type16 *type16;
768 type16_handle = *n;
769 smbios_generic_initializer(template_entry, template_strings,
770 curaddr, endaddr, n);
771 type16 = (struct smbios_table_type16 *)curaddr;
772 type16->xsize = guest_lomem + guest_himem;
773 type16->ndevs = guest_himem > 0 ? 2 : 1;
775 return (0);
778 static int
779 smbios_type17_initializer(const struct smbios_structure *template_entry,
780 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
781 uint16_t *n)
783 struct smbios_table_type17 *type17;
784 uint64_t memsize, size_KB, size_MB;
786 smbios_generic_initializer(template_entry, template_strings,
787 curaddr, endaddr, n);
788 type17 = (struct smbios_table_type17 *)curaddr;
789 type17->arrayhand = type16_handle;
791 memsize = guest_lomem + guest_himem;
792 size_KB = memsize / 1024;
793 size_MB = memsize / MB;
795 /* A single Type 17 entry can't represent more than ~2PB RAM */
796 if (size_MB > 0x7FFFFFFF) {
797 printf("Warning: guest memory too big for SMBIOS Type 17 table: "
798 "%luMB greater than max supported 2147483647MB\n", size_MB);
800 size_MB = 0x7FFFFFFF;
803 /* See SMBIOS 2.7.0 section 7.18 - Memory Device (Type 17) */
804 if (size_KB <= 0x7FFF) {
805 /* Can represent up to 32767KB with the top bit set */
806 type17->size = size_KB | (1 << 15);
807 } else if (size_MB < 0x7FFF) {
808 /* Can represent up to 32766MB with the top bit unset */
809 type17->size = size_MB & 0x7FFF;
810 } else {
811 type17->size = 0x7FFF;
813 * Can represent up to 2147483647MB (~2PB)
814 * The top bit is reserved
816 type17->xsize = size_MB & 0x7FFFFFFF;
819 return (0);
822 static int
823 smbios_type19_initializer(const struct smbios_structure *template_entry,
824 const struct smbios_string *template_strings, char *curaddr, char **endaddr,
825 uint16_t *n)
827 struct smbios_table_type19 *type19;
829 smbios_generic_initializer(template_entry, template_strings,
830 curaddr, endaddr, n);
831 type19 = (struct smbios_table_type19 *)curaddr;
832 type19->arrayhand = type16_handle;
833 type19->xsaddr = 0;
834 type19->xeaddr = guest_lomem;
836 if (guest_himem > 0) {
837 curaddr = *endaddr;
838 smbios_generic_initializer(template_entry, template_strings,
839 curaddr, endaddr, n);
840 type19 = (struct smbios_table_type19 *)curaddr;
841 type19->arrayhand = type16_handle;
842 type19->xsaddr = 4*GB;
843 type19->xeaddr = type19->xsaddr + guest_himem;
846 return (0);
849 static void
850 smbios_ep_initializer(struct smbios_entry_point *smbios_ep, uint32_t staddr)
852 memset(smbios_ep, 0, sizeof(*smbios_ep));
853 memcpy(smbios_ep->eanchor, SMBIOS_ENTRY_EANCHOR,
854 SMBIOS_ENTRY_EANCHORLEN);
855 smbios_ep->eplen = 0x1F;
856 assert(sizeof (struct smbios_entry_point) == smbios_ep->eplen);
857 smbios_ep->major = 2;
858 smbios_ep->minor = 6;
859 smbios_ep->revision = 0;
860 memcpy(smbios_ep->ianchor, SMBIOS_ENTRY_IANCHOR,
861 SMBIOS_ENTRY_IANCHORLEN);
862 smbios_ep->staddr = staddr;
863 smbios_ep->bcdrev = (smbios_ep->major & 0xf) << 4 | (smbios_ep->minor & 0xf);
866 static void
867 smbios_ep_finalizer(struct smbios_entry_point *smbios_ep, uint16_t len,
868 uint16_t num, uint16_t maxssize)
870 uint8_t checksum;
871 int i;
873 smbios_ep->maxssize = maxssize;
874 smbios_ep->stlen = len;
875 smbios_ep->stnum = num;
877 checksum = 0;
878 for (i = 0x10; i < 0x1f; i++) {
879 checksum -= ((uint8_t *)smbios_ep)[i];
881 smbios_ep->ichecksum = checksum;
883 checksum = 0;
884 for (i = 0; i < 0x1f; i++) {
885 checksum -= ((uint8_t *)smbios_ep)[i];
887 smbios_ep->echecksum = checksum;
890 #ifndef __FreeBSD__
892 * bhyve on illumos previously used configuration keys starting with 'smbios.'
893 * to control type 1 SMBIOS information. Since these may still be present in
894 * bhyve configuration files, the following table is used to translate them
895 * to their new key names.
897 static struct {
898 const char *oldkey;
899 const char *newkey;
900 } smbios_legacy_config_map[] = {
901 { "smbios.manufacturer", "system.manufacturer" },
902 { "smbios.family", "system.family_name" },
903 { "smbios.product", "system.product_name" },
904 { "smbios.serial", "system.serial_number" },
905 { "smbios.sku", "system.sku" },
906 { "smbios.version", "system.version" },
908 #endif
911 smbios_build(struct vmctx *ctx)
913 struct smbios_entry_point *smbios_ep;
914 uint16_t n;
915 uint16_t maxssize;
916 char *curaddr, *startaddr, *ststartaddr;
917 int i;
918 int err;
920 guest_lomem = vm_get_lowmem_size(ctx);
921 guest_himem = vm_get_highmem_size(ctx);
923 startaddr = paddr_guest2host(ctx, SMBIOS_BASE, SMBIOS_MAX_LENGTH);
924 if (startaddr == NULL) {
925 EPRINTLN("smbios table requires mapped mem");
926 return (ENOMEM);
929 #ifndef __FreeBSD__
930 /* Translate legacy illumos configuration keys */
931 for (uint_t i = 0; i < ARRAY_SIZE(smbios_legacy_config_map); i++) {
932 const char *v;
934 v = get_config_value(smbios_legacy_config_map[i].oldkey);
935 if (v != NULL) {
936 set_config_value_if_unset(
937 smbios_legacy_config_map[i].newkey, v);
940 #endif
942 curaddr = startaddr;
944 smbios_ep = (struct smbios_entry_point *)curaddr;
945 smbios_ep_initializer(smbios_ep, SMBIOS_BASE +
946 sizeof(struct smbios_entry_point));
947 curaddr += sizeof(struct smbios_entry_point);
948 ststartaddr = curaddr;
950 n = 0;
951 maxssize = 0;
952 for (i = 0; smbios_template[i].entry != NULL; i++) {
953 const struct smbios_structure *entry;
954 const struct smbios_string *strings;
955 initializer_func_t initializer;
956 char *endaddr;
957 size_t size;
959 entry = smbios_template[i].entry;
960 strings = smbios_template[i].strings;
961 initializer = smbios_template[i].initializer;
963 err = (*initializer)(entry, strings, curaddr, &endaddr, &n);
964 if (err != 0)
965 return (err);
967 size = endaddr - curaddr;
968 assert(size <= UINT16_MAX);
969 if (size > maxssize)
970 maxssize = (uint16_t)size;
971 curaddr = endaddr;
974 assert(curaddr - startaddr < SMBIOS_MAX_LENGTH);
975 smbios_ep_finalizer(smbios_ep, curaddr - ststartaddr, n, maxssize);
977 return (0);
980 #ifndef __FreeBSD__
981 static struct {
982 uint_t type;
983 const char *key;
984 char *val;
985 } smbios_legacy_map[] = {
986 { 1, "product", "product_name" },
987 { 1, "serial", "serial_number" },
988 { 1, "family", "family_name" },
991 static const struct smbios_string *smbios_tbl_map[] = {
992 smbios_type0_strings,
993 smbios_type1_strings,
994 smbios_type2_strings,
995 smbios_type3_strings,
999 * This function accepts an option of the form
1000 * type,[key=value][,key=value]...
1001 * and sets smbios data for the given type. Keys for type X are defined in the
1002 * smbios_typeX_strings tables above, but for type 1 there are also some
1003 * legacy values which were accepted in earlier versions of bhyve on illumos
1004 * which need to be mapped.
1007 smbios_parse(const char *opts)
1009 char *buf, *lasts, *token, *typekey = NULL;
1010 const char *errstr;
1011 const struct smbios_string *tbl;
1012 nvlist_t *nvl;
1013 uint_t i;
1014 long type;
1016 if ((buf = strdup(opts)) == NULL) {
1017 (void) fprintf(stderr, "out of memory\n");
1018 return (-1);
1021 if ((token = strtok_r(buf, ",", &lasts)) == NULL) {
1022 (void) fprintf(stderr, "too few fields\n");
1023 goto fail;
1026 type = strtonum(token, 0, 3, &errstr);
1027 if (errstr != NULL) {
1028 fprintf(stderr, "First token (type) is %s\n", errstr);
1029 goto fail;
1032 tbl = smbios_tbl_map[type];
1034 /* Extract the config key for this type */
1035 typekey = strdup(tbl[0].node);
1036 if (typekey == NULL) {
1037 (void) fprintf(stderr, "out of memory\n");
1038 goto fail;
1041 token = strchr(typekey, '.');
1042 assert(token != NULL);
1043 *token = '\0';
1045 nvl = create_config_node(typekey);
1046 if (nvl == NULL) {
1047 (void) fprintf(stderr, "out of memory\n");
1048 goto fail;
1051 while ((token = strtok_r(NULL, ",", &lasts)) != NULL) {
1052 char *val;
1054 if ((val = strchr(token, '=')) == NULL || val[1] == '\0') {
1055 (void) fprintf(stderr, "invalid key=value: '%s'\n",
1056 token);
1057 goto fail;
1059 *val++ = '\0';
1061 /* UUID is a top-level config item, but -U takes priority */
1062 if (strcmp(token, "uuid") == 0) {
1063 set_config_value_if_unset(token, val);
1064 continue;
1067 /* Translate legacy keys */
1068 for (i = 0; i < ARRAY_SIZE(smbios_legacy_map); i++) {
1069 if (type == smbios_legacy_map[i].type &&
1070 strcmp(token, smbios_legacy_map[i].key) == 0) {
1071 token = smbios_legacy_map[i].val;
1072 break;
1076 for (i = 0; tbl[i].value != NULL; i++) {
1077 if (strcmp(tbl[i].node + strlen(typekey) + 1,
1078 token) == 0) {
1079 /* Found match */
1080 break;
1084 if (tbl[i].value == NULL) {
1085 (void) fprintf(stderr,
1086 "Unknown SMBIOS key %s for type %d\n", token, type);
1087 goto fail;
1090 set_config_value_node(nvl, token, val);
1093 free(typekey);
1094 return (0);
1096 fail:
1097 free(buf);
1098 free(typekey);
1099 return (-1);
1101 #endif