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