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