]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/libsa/smbios.c
sqlite3: Vendor import of sqlite3 3.41.2
[FreeBSD/FreeBSD.git] / stand / libsa / smbios.c
1 /*-
2  * Copyright (c) 2005-2009 Jung-uk Kim <jkim@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *      notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *      notice, this list of conditions and the following disclaimer in the
12  *      documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <stand.h>
31 #include <sys/endian.h>
32
33 #define PTOV(x)         ptov(x)
34
35 /* Only enable 64-bit entry point if it makes sense */
36 #if __SIZEOF_POINTER__ > 4
37 #define HAS_SMBV3       1
38 #endif
39
40 /*
41  * Detect SMBIOS and export information about the SMBIOS into the
42  * environment.
43  *
44  * System Management BIOS Reference Specification, v2.6 Final
45  * http://www.dmtf.org/standards/published_documents/DSP0134_2.6.0.pdf
46  */
47
48 /*
49  * 2.1.1 SMBIOS Structure Table Entry Point
50  *
51  * "On non-EFI systems, the SMBIOS Entry Point structure, described below, can
52  * be located by application software by searching for the anchor-string on
53  * paragraph (16-byte) boundaries within the physical memory address range
54  * 000F0000h to 000FFFFFh. This entry point encapsulates an intermediate anchor
55  * string that is used by some existing DMI browsers."
56  */
57 #define SMBIOS_START            0xf0000
58 #define SMBIOS_LENGTH           0x10000
59 #define SMBIOS_STEP             0x10
60 #define SMBIOS_SIG              "_SM_"
61 #define SMBIOS3_SIG             "_SM3_"
62 #define SMBIOS_DMI_SIG          "_DMI_"
63
64 #define SMBIOS_GET8(base, off)  (*(uint8_t *)((base) + (off)))
65 #define SMBIOS_GET16(base, off) (*(uint16_t *)((base) + (off)))
66 #define SMBIOS_GET32(base, off) (*(uint32_t *)((base) + (off)))
67 #define SMBIOS_GET64(base, off) (*(uint64_t *)((base) + (off)))
68
69 #define SMBIOS_GETLEN(base)     SMBIOS_GET8(base, 0x01)
70 #define SMBIOS_GETSTR(base)     ((base) + SMBIOS_GETLEN(base))
71
72 struct smbios_attr {
73         int             probed;
74         caddr_t         addr;
75         size_t          length;
76         size_t          count;
77         int             major;
78         int             minor;
79         int             ver;
80         const char*     bios_vendor;
81         const char*     maker;
82         const char*     product;
83         uint32_t        enabled_memory;
84         uint32_t        old_enabled_memory;
85         uint8_t         enabled_sockets;
86         uint8_t         populated_sockets;
87 };
88
89 static struct smbios_attr smbios;
90 #ifdef HAS_SMBV3
91 static int isv3;
92 #endif
93
94 static uint8_t
95 smbios_checksum(const caddr_t addr, const uint8_t len)
96 {
97         uint8_t         sum;
98         int             i;
99
100         for (sum = 0, i = 0; i < len; i++)
101                 sum += SMBIOS_GET8(addr, i);
102         return (sum);
103 }
104
105 static caddr_t
106 smbios_sigsearch(const caddr_t addr, const uint32_t len)
107 {
108         caddr_t         cp;
109
110         /* Search on 16-byte boundaries. */
111         for (cp = addr; cp < addr + len; cp += SMBIOS_STEP) {
112                 /* v2.1, 32-bit Entry point */
113                 if (strncmp(cp, SMBIOS_SIG, sizeof(SMBIOS_SIG) - 1) == 0 &&
114                     smbios_checksum(cp, SMBIOS_GET8(cp, 0x05)) == 0 &&
115                     strncmp(cp + 0x10, SMBIOS_DMI_SIG, 5) == 0 &&
116                     smbios_checksum(cp + 0x10, 0x0f) == 0)
117                         return (cp);
118
119 #ifdef HAS_SMBV3
120                 /* v3.0, 64-bit Entry point */
121                 if (strncmp(cp, SMBIOS3_SIG, sizeof(SMBIOS3_SIG) - 1) == 0 &&
122                     smbios_checksum(cp, SMBIOS_GET8(cp, 0x06)) == 0) {
123                         isv3 = 1;
124                         return (cp);
125                 }
126 #endif
127         }
128         return (NULL);
129 }
130
131 static const char*
132 smbios_getstring(caddr_t addr, const int offset)
133 {
134         caddr_t         cp;
135         int             i, idx;
136
137         idx = SMBIOS_GET8(addr, offset);
138         if (idx != 0) {
139                 cp = SMBIOS_GETSTR(addr);
140                 for (i = 1; i < idx; i++)
141                         cp += strlen(cp) + 1;
142                 return cp;
143         }
144         return (NULL);
145 }
146
147 static void
148 smbios_setenv(const char *name, caddr_t addr, const int offset)
149 {
150         const char*     val;
151
152         val = smbios_getstring(addr, offset);
153         if (val != NULL)
154                 setenv(name, val, 1);
155 }
156
157 #ifdef SMBIOS_SERIAL_NUMBERS
158
159 #define UUID_SIZE               16
160 #define UUID_TYPE               uint32_t
161 #define UUID_STEP               sizeof(UUID_TYPE)
162 #define UUID_ALL_BITS           (UUID_SIZE / UUID_STEP)
163 #define UUID_GET(base, off)     (*(UUID_TYPE *)((base) + (off)))
164
165 static void
166 smbios_setuuid(const char *name, const caddr_t addr, const int ver)
167 {
168         char            uuid[37];
169         int             byteorder, i, ones, zeros;
170         UUID_TYPE       n;
171         uint32_t        f1;
172         uint16_t        f2, f3;
173
174         for (i = 0, ones = 0, zeros = 0; i < UUID_SIZE; i += UUID_STEP) {
175                 n = UUID_GET(addr, i) + 1;
176                 if (zeros == 0 && n == 0)
177                         ones++;
178                 else if (ones == 0 && n == 1)
179                         zeros++;
180                 else
181                         break;
182         }
183
184         if (ones != UUID_ALL_BITS && zeros != UUID_ALL_BITS) {
185                 /*
186                  * 3.3.2.1 System UUID
187                  *
188                  * "Although RFC 4122 recommends network byte order for all
189                  * fields, the PC industry (including the ACPI, UEFI, and
190                  * Microsoft specifications) has consistently used
191                  * little-endian byte encoding for the first three fields:
192                  * time_low, time_mid, time_hi_and_version. The same encoding,
193                  * also known as wire format, should also be used for the
194                  * SMBIOS representation of the UUID."
195                  *
196                  * Note: We use network byte order for backward compatibility
197                  * unless SMBIOS version is 2.6+ or little-endian is forced.
198                  */
199 #if defined(SMBIOS_LITTLE_ENDIAN_UUID)
200                 byteorder = LITTLE_ENDIAN;
201 #elif defined(SMBIOS_NETWORK_ENDIAN_UUID)
202                 byteorder = BIG_ENDIAN;
203 #else
204                 byteorder = ver < 0x0206 ? BIG_ENDIAN : LITTLE_ENDIAN;
205 #endif
206                 if (byteorder != LITTLE_ENDIAN) {
207                         f1 = ntohl(SMBIOS_GET32(addr, 0));
208                         f2 = ntohs(SMBIOS_GET16(addr, 4));
209                         f3 = ntohs(SMBIOS_GET16(addr, 6));
210                 } else {
211                         f1 = le32toh(SMBIOS_GET32(addr, 0));
212                         f2 = le16toh(SMBIOS_GET16(addr, 4));
213                         f3 = le16toh(SMBIOS_GET16(addr, 6));
214                 }
215                 sprintf(uuid,
216                     "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
217                     f1, f2, f3, SMBIOS_GET8(addr, 8), SMBIOS_GET8(addr, 9),
218                     SMBIOS_GET8(addr, 10), SMBIOS_GET8(addr, 11),
219                     SMBIOS_GET8(addr, 12), SMBIOS_GET8(addr, 13),
220                     SMBIOS_GET8(addr, 14), SMBIOS_GET8(addr, 15));
221                 setenv(name, uuid, 1);
222         }
223 }
224
225 #undef UUID_SIZE
226 #undef UUID_TYPE
227 #undef UUID_STEP
228 #undef UUID_ALL_BITS
229 #undef UUID_GET
230
231 #endif
232
233 static const char *
234 smbios_parse_chassis_type(caddr_t addr)
235 {
236         int             type;
237
238         type = SMBIOS_GET8(addr, 0x5);
239         switch (type) {
240         case 0x1:
241                 return ("Other");
242         case 0x2:
243                 return ("Unknown");
244         case 0x3:
245                 return ("Desktop");
246         case 0x4:
247                 return ("Low Profile Desktop");
248         case 0x5:
249                 return ("Pizza Box");
250         case 0x6:
251                 return ("Mini Tower");
252         case 0x7:
253                 return ("Tower");
254         case 0x8:
255                 return ("Portable");
256         case 0x9:
257                 return ("Laptop");
258         case 0xA:
259                 return ("Notebook");
260         case 0xB:
261                 return ("Hand Held");
262         case 0xC:
263                 return ("Docking Station");
264         case 0xD:
265                 return ("All in One");
266         case 0xE:
267                 return ("Sub Notebook");
268         case 0xF:
269                 return ("Lunch Box");
270         case 0x10:
271                 return ("Space-saving");
272         case 0x11:
273                 return ("Main Server Chassis");
274         case 0x12:
275                 return ("Expansion Chassis");
276         case 0x13:
277                 return ("SubChassis");
278         case 0x14:
279                 return ("Bus Expansion Chassis");
280         case 0x15:
281                 return ("Peripheral Chassis");
282         case 0x16:
283                 return ("RAID Chassis");
284         case 0x17:
285                 return ("Rack Mount Chassis");
286         case 0x18:
287                 return ("Sealed-case PC");
288         case 0x19:
289                 return ("Multi-system chassis");
290         case 0x1A:
291                 return ("Compact PCI");
292         case 0x1B:
293                 return ("Advanced TCA");
294         case 0x1C:
295                 return ("Blade");
296         case 0x1D:
297                 return ("Blade Enclosure");
298         case 0x1E:
299                 return ("Tablet");
300         case 0x1F:
301                 return ("Convertible");
302         case 0x20:
303                 return ("Detachable");
304         case 0x21:
305                 return ("IoT Gateway");
306         case 0x22:
307                 return ("Embedded PC");
308         case 0x23:
309                 return ("Mini PC");
310         case 0x24:
311                 return ("Stick PC");
312         }
313
314         return ("Undefined");
315 }
316
317 static caddr_t
318 smbios_parse_table(const caddr_t addr)
319 {
320         caddr_t         cp;
321         int             proc, size, osize, type;
322         uint8_t         bios_minor, bios_major;
323         char            buf[16];
324
325         type = SMBIOS_GET8(addr, 0);    /* 3.1.2 Structure Header Format */
326         switch(type) {
327         case 0:         /* 3.3.1 BIOS Information (Type 0) */
328                 smbios_setenv("smbios.bios.vendor", addr, 0x04);
329                 smbios_setenv("smbios.bios.version", addr, 0x05);
330                 smbios_setenv("smbios.bios.reldate", addr, 0x08);
331                 bios_major = SMBIOS_GET8(addr, 0x14);
332                 bios_minor = SMBIOS_GET8(addr, 0x15);
333                 if (bios_minor != 0xFF && bios_major != 0xFF) {
334                         snprintf(buf, sizeof(buf), "%u.%u",
335                             bios_major, bios_minor);
336                         setenv("smbios.bios.revision", buf, 1);
337                 }
338                 break;
339
340         case 1:         /* 3.3.2 System Information (Type 1) */
341                 smbios_setenv("smbios.system.maker", addr, 0x04);
342                 smbios_setenv("smbios.system.product", addr, 0x05);
343                 smbios_setenv("smbios.system.version", addr, 0x06);
344 #ifdef SMBIOS_SERIAL_NUMBERS
345                 smbios_setenv("smbios.system.serial", addr, 0x07);
346                 smbios_setuuid("smbios.system.uuid", addr + 0x08, smbios.ver);
347 #endif
348                 if (smbios.major > 2 ||
349                     (smbios.major == 2 && smbios.minor >= 4)) {
350                         smbios_setenv("smbios.system.sku", addr, 0x19);
351                         smbios_setenv("smbios.system.family", addr, 0x1a);
352                 }
353                 break;
354
355         case 2:         /* 3.3.3 Base Board (or Module) Information (Type 2) */
356                 smbios_setenv("smbios.planar.maker", addr, 0x04);
357                 smbios_setenv("smbios.planar.product", addr, 0x05);
358                 smbios_setenv("smbios.planar.version", addr, 0x06);
359 #ifdef SMBIOS_SERIAL_NUMBERS
360                 smbios_setenv("smbios.planar.serial", addr, 0x07);
361                 smbios_setenv("smbios.planar.tag", addr, 0x08);
362 #endif
363                 smbios_setenv("smbios.planar.location", addr, 0x0a);
364                 break;
365
366         case 3:         /* 3.3.4 System Enclosure or Chassis (Type 3) */
367                 smbios_setenv("smbios.chassis.maker", addr, 0x04);
368                 setenv("smbios.chassis.type", smbios_parse_chassis_type(addr), 1);
369                 smbios_setenv("smbios.chassis.version", addr, 0x06);
370 #ifdef SMBIOS_SERIAL_NUMBERS
371                 smbios_setenv("smbios.chassis.serial", addr, 0x07);
372                 smbios_setenv("smbios.chassis.tag", addr, 0x08);
373 #endif
374                 break;
375
376         case 4:         /* 3.3.5 Processor Information (Type 4) */
377                 /*
378                  * Offset 18h: Processor Status
379                  *
380                  * Bit 7        Reserved, must be 0
381                  * Bit 6        CPU Socket Populated
382                  *              1 - CPU Socket Populated
383                  *              0 - CPU Socket Unpopulated
384                  * Bit 5:3      Reserved, must be zero
385                  * Bit 2:0      CPU Status
386                  *              0h - Unknown
387                  *              1h - CPU Enabled
388                  *              2h - CPU Disabled by User via BIOS Setup
389                  *              3h - CPU Disabled by BIOS (POST Error)
390                  *              4h - CPU is Idle, waiting to be enabled
391                  *              5-6h - Reserved
392                  *              7h - Other
393                  */
394                 proc = SMBIOS_GET8(addr, 0x18);
395                 if ((proc & 0x07) == 1)
396                         smbios.enabled_sockets++;
397                 if ((proc & 0x40) != 0)
398                         smbios.populated_sockets++;
399                 break;
400
401         case 6:         /* 3.3.7 Memory Module Information (Type 6, Obsolete) */
402                 /*
403                  * Offset 0Ah: Enabled Size
404                  *
405                  * Bit 7        Bank connection
406                  *              1 - Double-bank connection
407                  *              0 - Single-bank connection
408                  * Bit 6:0      Size (n), where 2**n is the size in MB
409                  *              7Dh - Not determinable (Installed Size only)
410                  *              7Eh - Module is installed, but no memory
411                  *                    has been enabled
412                  *              7Fh - Not installed
413                  */
414                 osize = SMBIOS_GET8(addr, 0x0a) & 0x7f;
415                 if (osize > 0 && osize < 22)
416                         smbios.old_enabled_memory += 1 << (osize + 10);
417                 break;
418
419         case 17:        /* 3.3.18 Memory Device (Type 17) */
420                 /*
421                  * Offset 0Ch: Size
422                  *
423                  * Bit 15       Granularity
424                  *              1 - Value is in kilobytes units
425                  *              0 - Value is in megabytes units
426                  * Bit 14:0     Size
427                  */
428                 size = SMBIOS_GET16(addr, 0x0c);
429                 if (size != 0 && size != 0xffff)
430                         smbios.enabled_memory += (size & 0x8000) != 0 ?
431                             (size & 0x7fff) : (size << 10);
432                 break;
433
434         default:        /* skip other types */
435                 break;
436         }
437
438         /* Find structure terminator. */
439         cp = SMBIOS_GETSTR(addr);
440         while (SMBIOS_GET16(cp, 0) != 0)
441                 cp++;
442
443         return (cp + 2);
444 }
445
446 static caddr_t
447 smbios_find_struct(int type)
448 {
449         caddr_t         dmi;
450         size_t          i;
451
452         if (smbios.addr == NULL)
453                 return (NULL);
454
455         for (dmi = smbios.addr, i = 0;
456              dmi < smbios.addr + smbios.length && i < smbios.count; i++) {
457                 if (SMBIOS_GET8(dmi, 0) == type)
458                         return dmi;
459                 /* Find structure terminator. */
460                 dmi = SMBIOS_GETSTR(dmi);
461                 while (SMBIOS_GET16(dmi, 0) != 0)
462                         dmi++;
463                 dmi += 2;
464         }
465
466         return (NULL);
467 }
468
469 static void
470 smbios_probe(const caddr_t addr)
471 {
472         caddr_t         saddr, info;
473         uintptr_t       paddr;
474         int             maj_off;
475         int             min_off;
476
477         if (smbios.probed)
478                 return;
479         smbios.probed = 1;
480
481         /* Search signatures and validate checksums. */
482         saddr = smbios_sigsearch(addr ? addr : PTOV(SMBIOS_START),
483             SMBIOS_LENGTH);
484         if (saddr == NULL)
485                 return;
486
487 #ifdef HAS_SMBV3
488         if (isv3) {
489                 smbios.length = SMBIOS_GET32(saddr, 0x0c);      /* Structure Table Length */
490                 paddr = SMBIOS_GET64(saddr, 0x10);              /* Structure Table Address */
491                 smbios.count = -1;                              /* not present in V3 */
492                 smbios.ver = 0;                                 /* not present in V3 */
493                 maj_off = 0x07;
494                 min_off = 0x08;
495         } else
496 #endif
497         {
498                 smbios.length = SMBIOS_GET16(saddr, 0x16);      /* Structure Table Length */
499                 paddr = SMBIOS_GET32(saddr, 0x18);              /* Structure Table Address */
500                 smbios.count = SMBIOS_GET16(saddr, 0x1c);       /* No of SMBIOS Structures */
501                 smbios.ver = SMBIOS_GET8(saddr, 0x1e);          /* SMBIOS BCD Revision */
502                 maj_off = 0x06;
503                 min_off = 0x07;
504         }
505
506
507         if (smbios.ver != 0) {
508                 smbios.major = smbios.ver >> 4;
509                 smbios.minor = smbios.ver & 0x0f;
510                 if (smbios.major > 9 || smbios.minor > 9)
511                         smbios.ver = 0;
512         }
513         if (smbios.ver == 0) {
514                 smbios.major = SMBIOS_GET8(saddr, maj_off);/* SMBIOS Major Version */
515                 smbios.minor = SMBIOS_GET8(saddr, min_off);/* SMBIOS Minor Version */
516         }
517         smbios.ver = (smbios.major << 8) | smbios.minor;
518         smbios.addr = PTOV(paddr);
519
520         /* Get system information from SMBIOS */
521         info = smbios_find_struct(0x00);
522         if (info != NULL) {
523                 smbios.bios_vendor = smbios_getstring(info, 0x04);
524         }
525         info = smbios_find_struct(0x01);
526         if (info != NULL) {
527                 smbios.maker = smbios_getstring(info, 0x04);
528                 smbios.product = smbios_getstring(info, 0x05);
529         }
530 }
531
532 void
533 smbios_detect(const caddr_t addr)
534 {
535         char            buf[16];
536         caddr_t         dmi;
537         size_t          i;
538
539         smbios_probe(addr);
540         if (smbios.addr == NULL)
541                 return;
542
543         for (dmi = smbios.addr, i = 0;
544              dmi < smbios.addr + smbios.length && i < smbios.count; i++)
545                 dmi = smbios_parse_table(dmi);
546
547         sprintf(buf, "%d.%d", smbios.major, smbios.minor);
548         setenv("smbios.version", buf, 1);
549         if (smbios.enabled_memory > 0 || smbios.old_enabled_memory > 0) {
550                 sprintf(buf, "%u", smbios.enabled_memory > 0 ?
551                     smbios.enabled_memory : smbios.old_enabled_memory);
552                 setenv("smbios.memory.enabled", buf, 1);
553         }
554         if (smbios.enabled_sockets > 0) {
555                 sprintf(buf, "%u", smbios.enabled_sockets);
556                 setenv("smbios.socket.enabled", buf, 1);
557         }
558         if (smbios.populated_sockets > 0) {
559                 sprintf(buf, "%u", smbios.populated_sockets);
560                 setenv("smbios.socket.populated", buf, 1);
561         }
562 }
563
564 static int
565 smbios_match_str(const char* s1, const char* s2)
566 {
567         return (s1 == NULL || (s2 != NULL && !strcmp(s1, s2)));
568 }
569
570 int
571 smbios_match(const char* bios_vendor, const char* maker,
572     const char* product)
573 {
574         /* XXXRP currently, only called from non-EFI. */
575         smbios_probe(NULL);
576         return (smbios_match_str(bios_vendor, smbios.bios_vendor) &&
577             smbios_match_str(maker, smbios.maker) &&
578             smbios_match_str(product, smbios.product));
579 }