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