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