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