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