]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/smbiostbl.c
sqlite3: Vendor import of sqlite3 3.39.3
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / smbiostbl.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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)(struct smbios_structure *template_entry,
85     struct smbios_string *template_strings, char *curaddr, char **endaddr,
86     uint16_t *n, uint16_t *size);
87
88 struct smbios_template_entry {
89         struct smbios_structure *entry;
90         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 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 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 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(struct smbios_structure *template_entry,
378     struct smbios_string *template_strings, char *curaddr, char **endaddr,
379     uint16_t *n, uint16_t *size);
380
381 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 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 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 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 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 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 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(struct smbios_structure *template_entry,
481     struct smbios_string *template_strings, char *curaddr, char **endaddr,
482     uint16_t *n, uint16_t *size);
483
484 struct smbios_table_type16 smbios_type16_template = {
485         { SMBIOS_TYPE_MEMARRAY, sizeof (struct smbios_table_type16),  0 },
486         SMBIOS_MAL_SYSMB,
487         SMBIOS_MAU_SYSTEM,
488         SMBIOS_MAE_NONE,
489         0x80000000,     /* max mem capacity in kb (0x80000000=use extended) */
490         -1,             /* handle of error (if any) */
491         0,              /* number of slots or sockets (TBD) */
492         0               /* extended maximum memory capacity in bytes (TBD) */
493 };
494
495 static int smbios_type16_initializer(struct smbios_structure *template_entry,
496     struct smbios_string *template_strings, char *curaddr, char **endaddr,
497     uint16_t *n, uint16_t *size);
498
499 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 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(struct smbios_structure *template_entry,
536     struct smbios_string *template_strings, char *curaddr, char **endaddr,
537     uint16_t *n, uint16_t *size);
538
539 struct smbios_table_type19 smbios_type19_template = {
540         { SMBIOS_TYPE_MEMARRAYMAP, sizeof (struct smbios_table_type19),  0 },
541         0xffffffff,     /* starting phys addr in kb (0xffffffff=use ext) */
542         0xffffffff,     /* ending phys addr in kb (0xffffffff=use ext) */
543         -1,             /* physical memory array handle */
544         1,              /* number of devices that form a row */
545         0,              /* extended starting phys addr in bytes (TDB) */
546         0               /* extended ending phys addr in bytes (TDB) */
547 };
548
549 static int smbios_type19_initializer(struct smbios_structure *template_entry,
550     struct smbios_string *template_strings, char *curaddr, char **endaddr,
551     uint16_t *n, uint16_t *size);
552
553 struct smbios_table_type32 smbios_type32_template = {
554         { SMBIOS_TYPE_BOOT, sizeof (struct smbios_table_type32),  0 },
555         { 0, 0, 0, 0, 0, 0 },
556         SMBIOS_BOOT_NORMAL
557 };
558
559 struct smbios_table_type127 smbios_type127_template = {
560         { SMBIOS_TYPE_EOT, sizeof (struct smbios_table_type127),  0 }
561 };
562
563 static int smbios_generic_initializer(struct smbios_structure *template_entry,
564     struct smbios_string *template_strings, char *curaddr, char **endaddr,
565     uint16_t *n, uint16_t *size);
566
567 static struct smbios_template_entry smbios_template[] = {
568         { (struct smbios_structure *)&smbios_type0_template,
569           smbios_type0_strings,
570           smbios_generic_initializer },
571         { (struct smbios_structure *)&smbios_type1_template,
572           smbios_type1_strings,
573           smbios_type1_initializer },
574         { (struct smbios_structure *)&smbios_type2_template,
575           smbios_type2_strings,
576           smbios_generic_initializer },
577         { (struct smbios_structure *)&smbios_type3_template,
578           smbios_type3_strings,
579           smbios_generic_initializer },
580         { (struct smbios_structure *)&smbios_type4_template,
581           smbios_type4_strings,
582           smbios_type4_initializer },
583         { (struct smbios_structure *)&smbios_type16_template,
584           NULL,
585           smbios_type16_initializer },
586         { (struct smbios_structure *)&smbios_type17_template,
587           smbios_type17_strings,
588           smbios_type17_initializer },
589         { (struct smbios_structure *)&smbios_type19_template,
590           NULL,
591           smbios_type19_initializer },
592         { (struct smbios_structure *)&smbios_type32_template,
593           NULL,
594           smbios_generic_initializer },
595         { (struct smbios_structure *)&smbios_type127_template,
596           NULL,
597           smbios_generic_initializer },
598         { NULL,NULL, NULL }
599 };
600
601 static uint64_t guest_lomem, guest_himem;
602 static uint16_t type16_handle;
603
604 static int
605 smbios_generic_initializer(struct smbios_structure *template_entry,
606     struct smbios_string *template_strings, char *curaddr, char **endaddr,
607     uint16_t *n, uint16_t *size)
608 {
609         struct smbios_structure *entry;
610
611         memcpy(curaddr, template_entry, template_entry->length);
612         entry = (struct smbios_structure *)curaddr;
613         entry->handle = *n + 1;
614         curaddr += entry->length;
615         if (template_strings != NULL) {
616                 int     i;
617
618                 for (i = 0; template_strings[i].value != NULL; i++) {
619                         const char *string;
620                         int len;
621
622                         if (template_strings[i].node == NULL) {
623                                 string = template_strings[i].value;
624                         } else {
625                                 set_config_value_if_unset(
626                                     template_strings[i].node,
627                                     template_strings[i].value);
628                                 string = get_config_value(
629                                     template_strings[i].node);
630                         }
631
632                         len = strlen(string) + 1;
633                         memcpy(curaddr, string, len);
634                         curaddr += len;
635                 }
636                 *curaddr = '\0';
637                 curaddr++;
638         } else {
639                 /* Minimum string section is double nul */
640                 *curaddr = '\0';
641                 curaddr++;
642                 *curaddr = '\0';
643                 curaddr++;
644         }
645         (*n)++;
646         *endaddr = curaddr;
647
648         return (0);
649 }
650
651 static int
652 smbios_type1_initializer(struct smbios_structure *template_entry,
653     struct smbios_string *template_strings, char *curaddr, char **endaddr,
654     uint16_t *n, uint16_t *size)
655 {
656         struct smbios_table_type1 *type1;
657         const char *guest_uuid_str;
658
659         smbios_generic_initializer(template_entry, template_strings,
660             curaddr, endaddr, n, size);
661         type1 = (struct smbios_table_type1 *)curaddr;
662
663         guest_uuid_str = get_config_value("uuid");
664         if (guest_uuid_str != NULL) {
665                 uuid_t          uuid;
666                 uint32_t        status;
667
668                 uuid_from_string(guest_uuid_str, &uuid, &status);
669                 if (status != uuid_s_ok) {
670                         EPRINTLN("Invalid UUID");
671                         return (-1);
672                 }
673
674                 uuid_enc_le(&type1->uuid, &uuid);
675         } else {
676                 MD5_CTX         mdctx;
677                 u_char          digest[16];
678                 char            hostname[MAXHOSTNAMELEN];
679                 const char      *vmname;
680
681                 /*
682                  * Universally unique and yet reproducible are an
683                  * oxymoron, however reproducible is desirable in
684                  * this case.
685                  */
686                 if (gethostname(hostname, sizeof(hostname)))
687                         return (-1);
688
689                 MD5Init(&mdctx);
690                 vmname = get_config_value("name");
691                 MD5Update(&mdctx, vmname, strlen(vmname));
692                 MD5Update(&mdctx, hostname, sizeof(hostname));
693                 MD5Final(digest, &mdctx);
694
695                 /*
696                  * Set the variant and version number.
697                  */
698                 digest[6] &= 0x0F;
699                 digest[6] |= 0x30;      /* version 3 */
700                 digest[8] &= 0x3F;
701                 digest[8] |= 0x80;
702
703                 memcpy(&type1->uuid, digest, sizeof (digest));
704         }
705
706         return (0);
707 }
708
709 static int
710 smbios_type4_initializer(struct smbios_structure *template_entry,
711     struct smbios_string *template_strings, char *curaddr, char **endaddr,
712     uint16_t *n, uint16_t *size)
713 {
714         int i;
715
716         for (i = 0; i < sockets; i++) {
717                 struct smbios_table_type4 *type4;
718                 char *p;
719                 int nstrings, len;
720
721                 smbios_generic_initializer(template_entry, template_strings,
722                     curaddr, endaddr, n, size);
723                 type4 = (struct smbios_table_type4 *)curaddr;
724                 p = curaddr + sizeof (struct smbios_table_type4);
725                 nstrings = 0;
726                 while (p < *endaddr - 1) {
727                         if (*p++ == '\0')
728                                 nstrings++;
729                 }
730                 len = sprintf(*endaddr - 1, "CPU #%d", i) + 1;
731                 *endaddr += len - 1;
732                 *(*endaddr) = '\0';
733                 (*endaddr)++;
734                 type4->socket = nstrings + 1;
735                 /* Revise cores and threads after update to smbios 3.0 */
736                 if (cores > 254)
737                         type4->cores = 0;
738                 else
739                         type4->cores = cores;
740                 /* This threads is total threads in a socket */
741                 if ((cores * threads) > 254)
742                         type4->threads = 0;
743                 else
744                         type4->threads = (cores * threads);
745                 curaddr = *endaddr;
746         }
747
748         return (0);
749 }
750
751 static int
752 smbios_type16_initializer(struct smbios_structure *template_entry,
753     struct smbios_string *template_strings, char *curaddr, char **endaddr,
754     uint16_t *n, uint16_t *size)
755 {
756         struct smbios_table_type16 *type16;
757
758         type16_handle = *n;
759         smbios_generic_initializer(template_entry, template_strings,
760             curaddr, endaddr, n, size);
761         type16 = (struct smbios_table_type16 *)curaddr;
762         type16->xsize = guest_lomem + guest_himem;
763         type16->ndevs = guest_himem > 0 ? 2 : 1;
764
765         return (0);
766 }
767
768 static int
769 smbios_type17_initializer(struct smbios_structure *template_entry,
770     struct smbios_string *template_strings, char *curaddr, char **endaddr,
771     uint16_t *n, uint16_t *size)
772 {
773         struct smbios_table_type17 *type17;
774         uint64_t memsize, size_KB, size_MB;
775
776         smbios_generic_initializer(template_entry, template_strings,
777             curaddr, endaddr, n, size);
778         type17 = (struct smbios_table_type17 *)curaddr;
779         type17->arrayhand = type16_handle;
780
781         memsize = guest_lomem + guest_himem;
782         size_KB = memsize / 1024;
783         size_MB = memsize / MB;
784
785         /* A single Type 17 entry can't represent more than ~2PB RAM */
786         if (size_MB > 0x7FFFFFFF) {
787                 printf("Warning: guest memory too big for SMBIOS Type 17 table: "
788                         "%luMB greater than max supported 2147483647MB\n", size_MB);
789
790                 size_MB = 0x7FFFFFFF;
791         }
792
793         /* See SMBIOS 2.7.0 section 7.18 - Memory Device (Type 17) */
794         if (size_KB <= 0x7FFF) {
795                 /* Can represent up to 32767KB with the top bit set */
796                 type17->size = size_KB | (1 << 15);
797         } else if (size_MB < 0x7FFF) {
798                 /* Can represent up to 32766MB with the top bit unset */
799                 type17->size = size_MB & 0x7FFF;
800         } else {
801                 type17->size = 0x7FFF;
802                 /*
803                  * Can represent up to 2147483647MB (~2PB)
804                  * The top bit is reserved
805                  */
806                 type17->xsize = size_MB & 0x7FFFFFFF;
807         }
808
809         return (0);
810 }
811
812 static int
813 smbios_type19_initializer(struct smbios_structure *template_entry,
814     struct smbios_string *template_strings, char *curaddr, char **endaddr,
815     uint16_t *n, uint16_t *size)
816 {
817         struct smbios_table_type19 *type19;
818
819         smbios_generic_initializer(template_entry, template_strings,
820             curaddr, endaddr, n, size);
821         type19 = (struct smbios_table_type19 *)curaddr;
822         type19->arrayhand = type16_handle;
823         type19->xsaddr = 0;
824         type19->xeaddr = guest_lomem;
825
826         if (guest_himem > 0) {
827                 curaddr = *endaddr;
828                 smbios_generic_initializer(template_entry, template_strings,
829                     curaddr, endaddr, n, size);
830                 type19 = (struct smbios_table_type19 *)curaddr;
831                 type19->arrayhand = type16_handle;
832                 type19->xsaddr = 4*GB;
833                 type19->xeaddr = type19->xsaddr + guest_himem;
834         }
835
836         return (0);
837 }
838
839 static void
840 smbios_ep_initializer(struct smbios_entry_point *smbios_ep, uint32_t staddr)
841 {
842         memset(smbios_ep, 0, sizeof(*smbios_ep));
843         memcpy(smbios_ep->eanchor, SMBIOS_ENTRY_EANCHOR,
844             SMBIOS_ENTRY_EANCHORLEN);
845         smbios_ep->eplen = 0x1F;
846         assert(sizeof (struct smbios_entry_point) == smbios_ep->eplen);
847         smbios_ep->major = 2;
848         smbios_ep->minor = 6;
849         smbios_ep->revision = 0;
850         memcpy(smbios_ep->ianchor, SMBIOS_ENTRY_IANCHOR,
851             SMBIOS_ENTRY_IANCHORLEN);
852         smbios_ep->staddr = staddr;
853         smbios_ep->bcdrev = (smbios_ep->major & 0xf) << 4 | (smbios_ep->minor & 0xf);
854 }
855
856 static void
857 smbios_ep_finalizer(struct smbios_entry_point *smbios_ep, uint16_t len,
858     uint16_t num, uint16_t maxssize)
859 {
860         uint8_t checksum;
861         int     i;
862
863         smbios_ep->maxssize = maxssize;
864         smbios_ep->stlen = len;
865         smbios_ep->stnum = num;
866
867         checksum = 0;
868         for (i = 0x10; i < 0x1f; i++) {
869                 checksum -= ((uint8_t *)smbios_ep)[i];
870         }
871         smbios_ep->ichecksum = checksum;
872
873         checksum = 0;
874         for (i = 0; i < 0x1f; i++) {
875                 checksum -= ((uint8_t *)smbios_ep)[i];
876         }
877         smbios_ep->echecksum = checksum;
878 }
879
880 int
881 smbios_build(struct vmctx *ctx)
882 {
883         struct smbios_entry_point       *smbios_ep;
884         uint16_t                        n;
885         uint16_t                        maxssize;
886         char                            *curaddr, *startaddr, *ststartaddr;
887         int                             i;
888         int                             err;
889
890         guest_lomem = vm_get_lowmem_size(ctx);
891         guest_himem = vm_get_highmem_size(ctx);
892
893         startaddr = paddr_guest2host(ctx, SMBIOS_BASE, SMBIOS_MAX_LENGTH);
894         if (startaddr == NULL) {
895                 EPRINTLN("smbios table requires mapped mem");
896                 return (ENOMEM);
897         }
898
899         curaddr = startaddr;
900
901         smbios_ep = (struct smbios_entry_point *)curaddr;
902         smbios_ep_initializer(smbios_ep, SMBIOS_BASE +
903             sizeof(struct smbios_entry_point));
904         curaddr += sizeof(struct smbios_entry_point);
905         ststartaddr = curaddr;
906
907         n = 0;
908         maxssize = 0;
909         for (i = 0; smbios_template[i].entry != NULL; i++) {
910                 struct smbios_structure *entry;
911                 struct smbios_string    *strings;
912                 initializer_func_t      initializer;
913                 char                    *endaddr;
914                 uint16_t                size;
915
916                 entry = smbios_template[i].entry;
917                 strings = smbios_template[i].strings;
918                 initializer = smbios_template[i].initializer;
919
920                 err = (*initializer)(entry, strings, curaddr, &endaddr,
921                     &n, &size);
922                 if (err != 0)
923                         return (err);
924
925                 if (size > maxssize)
926                         maxssize = size;
927
928                 curaddr = endaddr;
929         }
930
931         assert(curaddr - startaddr < SMBIOS_MAX_LENGTH);
932         smbios_ep_finalizer(smbios_ep, curaddr - ststartaddr, n, maxssize);
933
934         return (0);
935 }