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