1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * System Memory information
4  *
5  *   Copyright (C) 2000-2002 Alan Cox <alan@redhat.com>
6  *   Copyright (C) 2002-2020 Jean Delvare <jdelvare@suse.de>
7  *   Copyright (C) 2020 Bastien Nocera <hadess@hadess.net>
8  *
9  * Unless specified otherwise, all references are aimed at the "System
10  * Management BIOS Reference Specification, Version 3.2.0" document,
11  * available from http://www.dmtf.org/standards/smbios.
12  *
13  * Note to contributors:
14  * Please reference every value you add or modify, especially if the
15  * information does not come from the above mentioned specification.
16  *
17  * Additional references:
18  *  - Intel AP-485 revision 36
19  *    "Intel Processor Identification and the CPUID Instruction"
20  *    http://www.intel.com/support/processors/sb/cs-009861.htm
21  *  - DMTF Common Information Model
22  *    CIM Schema version 2.19.1
23  *    http://www.dmtf.org/standards/cim/
24  *  - IPMI 2.0 revision 1.0
25  *    "Intelligent Platform Management Interface Specification"
26  *    http://developer.intel.com/design/servers/ipmi/spec.htm
27  *  - AMD publication #25481 revision 2.28
28  *    "CPUID Specification"
29  *    http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf
30  *  - BIOS Integrity Services Application Programming Interface version 1.0
31  *    http://www.intel.com/design/archives/wfm/downloads/bisspec.htm
32  *  - DMTF DSP0239 version 1.1.0
33  *    "Management Component Transport Protocol (MCTP) IDs and Codes"
34  *    http://www.dmtf.org/standards/pmci
35  *  - "TPM Main, Part 2 TPM Structures"
36  *    Specification version 1.2, level 2, revision 116
37  *    https://trustedcomputinggroup.org/tpm-main-specification/
38  *  - "PC Client Platform TPM Profile (PTP) Specification"
39  *    Family "2.0", Level 00, Revision 00.43, January 26, 2015
40  *    https://trustedcomputinggroup.org/pc-client-platform-tpm-profile-ptp-specification/
41  *  - "RedFish Host Interface Specification" (DMTF DSP0270)
42  *    https://www.dmtf.org/sites/default/files/DSP0270_1.0.1.pdf
43  */
44 
45 #include <getopt.h>
46 
47 #include "alloc-util.h"
48 #include "fileio.h"
49 #include "main-func.h"
50 #include "string-util.h"
51 #include "udev-util.h"
52 #include "unaligned.h"
53 #include "version.h"
54 
55 #define SUPPORTED_SMBIOS_VER 0x030300
56 
57 #define OUT_OF_SPEC_STR "<OUT OF SPEC>"
58 
59 #define SYS_FIRMWARE_DIR "/sys/firmware/dmi/tables"
60 #define SYS_ENTRY_FILE SYS_FIRMWARE_DIR "/smbios_entry_point"
61 #define SYS_TABLE_FILE SYS_FIRMWARE_DIR "/DMI"
62 
63 /*
64  * Per SMBIOS v2.8.0 and later, all structures assume a little-endian
65  * ordering convention.
66  */
67 #define WORD(x)  (unaligned_read_le16(x))
68 #define DWORD(x) (unaligned_read_le32(x))
69 #define QWORD(x) (unaligned_read_le64(x))
70 
71 struct dmi_header {
72         uint8_t type;
73         uint8_t length;
74         uint16_t handle;
75         const uint8_t *data;
76 };
77 
78 static const char *arg_source_file = NULL;
79 
verify_checksum(const uint8_t * buf,size_t len)80 static bool verify_checksum(const uint8_t *buf, size_t len) {
81         uint8_t sum = 0;
82 
83         for (size_t a = 0; a < len; a++)
84                 sum += buf[a];
85         return sum == 0;
86 }
87 
88 /*
89  * Type-independant Stuff
90  */
91 
dmi_string(const struct dmi_header * dm,uint8_t s)92 static const char *dmi_string(const struct dmi_header *dm, uint8_t s) {
93         const char *bp = (const char *) dm->data;
94 
95         if (s == 0)
96                 return "Not Specified";
97 
98         bp += dm->length;
99         for (;s > 1 && !isempty(bp); s--)
100                 bp += strlen(bp) + 1;
101 
102         if (isempty(bp))
103                 return "<BAD INDEX>";
104 
105         return bp;
106 }
107 
108 typedef enum {
109         MEMORY_SIZE_UNIT_BYTES,
110         MEMORY_SIZE_UNIT_KB
111 } MemorySizeUnit;
112 
dmi_print_memory_size(const char * attr_prefix,const char * attr_suffix,int slot_num,uint64_t code,MemorySizeUnit unit)113 static void dmi_print_memory_size(
114                 const char *attr_prefix, const char *attr_suffix,
115                 int slot_num, uint64_t code, MemorySizeUnit unit) {
116         if (unit == MEMORY_SIZE_UNIT_KB)
117                 code <<= 10;
118 
119         if (slot_num >= 0)
120                 printf("%s_%u_%s=%"PRIu64"\n", attr_prefix, slot_num, attr_suffix, code);
121         else
122                 printf("%s_%s=%"PRIu64"\n", attr_prefix, attr_suffix, code);
123 }
124 
125 /*
126  * 7.17 Physical Memory Array (Type 16)
127  */
128 
dmi_memory_array_location(uint8_t code)129 static void dmi_memory_array_location(uint8_t code) {
130         /* 7.17.1 */
131         static const char *location[] = {
132                 [0x01] = "Other",
133                 [0x02] = "Unknown",
134                 [0x03] = "System Board Or Motherboard",
135                 [0x04] = "ISA Add-on Card",
136                 [0x05] = "EISA Add-on Card",
137                 [0x06] = "PCI Add-on Card",
138                 [0x07] = "MCA Add-on Card",
139                 [0x08] = "PCMCIA Add-on Card",
140                 [0x09] = "Proprietary Add-on Card",
141                 [0x0A] = "NuBus",
142         };
143         static const char *location_0xA0[] = {
144                 [0x00] = "PC-98/C20 Add-on Card",       /* 0xA0 */
145                 [0x01] = "PC-98/C24 Add-on Card",       /* 0xA1 */
146                 [0x02] = "PC-98/E Add-on Card",         /* 0xA2 */
147                 [0x03] = "PC-98/Local Bus Add-on Card", /* 0xA3 */
148                 [0x04] = "CXL Flexbus 1.0",             /* 0xA4 */
149         };
150         const char *str = OUT_OF_SPEC_STR;
151 
152         if (code < ELEMENTSOF(location) && location[code])
153                 str = location[code];
154         else if (code >= 0xA0 && code < (ELEMENTSOF(location_0xA0) + 0xA0))
155                 str = location_0xA0[code - 0xA0];
156 
157         printf("MEMORY_ARRAY_LOCATION=%s\n", str);
158 }
159 
dmi_memory_array_ec_type(uint8_t code)160 static void dmi_memory_array_ec_type(uint8_t code) {
161         /* 7.17.3 */
162         static const char *type[] = {
163                 [0x01] = "Other",
164                 [0x02] = "Unknown",
165                 [0x03] = "None",
166                 [0x04] = "Parity",
167                 [0x05] = "Single-bit ECC",
168                 [0x06] = "Multi-bit ECC",
169                 [0x07] = "CRC",
170         };
171 
172         if (code != 0x03) /* Do not print "None". */
173                 printf("MEMORY_ARRAY_EC_TYPE=%s\n",
174                        code < ELEMENTSOF(type) && type[code] ? type[code] : OUT_OF_SPEC_STR);
175 }
176 
177 /*
178  * 7.18 Memory Device (Type 17)
179  */
180 
dmi_memory_device_string(const char * attr_suffix,unsigned slot_num,const struct dmi_header * h,uint8_t s)181 static void dmi_memory_device_string(
182                 const char *attr_suffix, unsigned slot_num,
183                 const struct dmi_header *h, uint8_t s) {
184         char *str;
185 
186         str = strdupa_safe(dmi_string(h, s));
187         str = strstrip(str);
188         if (!isempty(str))
189                 printf("MEMORY_DEVICE_%u_%s=%s\n", slot_num, attr_suffix, str);
190 }
191 
dmi_memory_device_width(const char * attr_suffix,unsigned slot_num,uint16_t code)192 static void dmi_memory_device_width(
193                 const char *attr_suffix,
194                 unsigned slot_num, uint16_t code) {
195 
196         /* If no memory module is present, width may be 0 */
197         if (!IN_SET(code, 0, 0xFFFF))
198                 printf("MEMORY_DEVICE_%u_%s=%u\n", slot_num, attr_suffix, code);
199 }
200 
dmi_memory_device_size(unsigned slot_num,uint16_t code)201 static void dmi_memory_device_size(unsigned slot_num, uint16_t code) {
202         if (code == 0)
203                 return (void) printf("MEMORY_DEVICE_%u_PRESENT=0\n", slot_num);
204         if (code == 0xFFFF)
205                 return;
206 
207         uint64_t s = code & 0x7FFF;
208         if (!(code & 0x8000))
209                 s <<= 10;
210         dmi_print_memory_size("MEMORY_DEVICE", "SIZE", slot_num, s, MEMORY_SIZE_UNIT_KB);
211 }
212 
dmi_memory_device_extended_size(unsigned slot_num,uint32_t code)213 static void dmi_memory_device_extended_size(unsigned slot_num, uint32_t code) {
214         uint64_t capacity = (uint64_t) code * 1024 * 1024;
215 
216         printf("MEMORY_DEVICE_%u_SIZE=%"PRIu64"\n", slot_num, capacity);
217 }
218 
dmi_memory_device_rank(unsigned slot_num,uint8_t code)219 static void dmi_memory_device_rank(unsigned slot_num, uint8_t code) {
220         code &= 0x0F;
221         if (code != 0)
222                 printf("MEMORY_DEVICE_%u_RANK=%u\n", slot_num, code);
223 }
224 
dmi_memory_device_voltage_value(const char * attr_suffix,unsigned slot_num,uint16_t code)225 static void dmi_memory_device_voltage_value(
226                 const char *attr_suffix,
227                 unsigned slot_num, uint16_t code) {
228         if (code == 0)
229                 return;
230         if (code % 100 != 0)
231                 printf("MEMORY_DEVICE_%u_%s=%g\n", slot_num, attr_suffix, (double)code / 1000);
232         else
233                 printf("MEMORY_DEVICE_%u_%s=%.1g\n", slot_num, attr_suffix, (double)code / 1000);
234 }
235 
dmi_memory_device_form_factor(unsigned slot_num,uint8_t code)236 static void dmi_memory_device_form_factor(unsigned slot_num, uint8_t code) {
237         /* 7.18.1 */
238         static const char *form_factor[] = {
239                 [0x01] = "Other",
240                 [0x02] = "Unknown",
241                 [0x03] = "SIMM",
242                 [0x04] = "SIP",
243                 [0x05] = "Chip",
244                 [0x06] = "DIP",
245                 [0x07] = "ZIP",
246                 [0x08] = "Proprietary Card",
247                 [0x09] = "DIMM",
248                 [0x0A] = "TSOP",
249                 [0x0B] = "Row Of Chips",
250                 [0x0C] = "RIMM",
251                 [0x0D] = "SODIMM",
252                 [0x0E] = "SRIMM",
253                 [0x0F] = "FB-DIMM",
254                 [0x10] = "Die",
255         };
256 
257         printf("MEMORY_DEVICE_%u_FORM_FACTOR=%s\n", slot_num,
258                code < ELEMENTSOF(form_factor) && form_factor[code] ? form_factor[code] : OUT_OF_SPEC_STR);
259 }
260 
dmi_memory_device_set(unsigned slot_num,uint8_t code)261 static void dmi_memory_device_set(unsigned slot_num, uint8_t code) {
262         if (code == 0xFF)
263                 printf("MEMORY_DEVICE_%u_SET=%s\n", slot_num, "Unknown");
264         else if (code != 0)
265                 printf("MEMORY_DEVICE_%u_SET=%"PRIu8"\n", slot_num, code);
266 }
267 
dmi_memory_device_type(unsigned slot_num,uint8_t code)268 static void dmi_memory_device_type(unsigned slot_num, uint8_t code) {
269         /* 7.18.2 */
270         static const char *type[] = {
271                 [0x01] = "Other",
272                 [0x02] = "Unknown",
273                 [0x03] = "DRAM",
274                 [0x04] = "EDRAM",
275                 [0x05] = "VRAM",
276                 [0x06] = "SRAM",
277                 [0x07] = "RAM",
278                 [0x08] = "ROM",
279                 [0x09] = "Flash",
280                 [0x0A] = "EEPROM",
281                 [0x0B] = "FEPROM",
282                 [0x0C] = "EPROM",
283                 [0x0D] = "CDRAM",
284                 [0x0E] = "3DRAM",
285                 [0x0F] = "SDRAM",
286                 [0x10] = "SGRAM",
287                 [0x11] = "RDRAM",
288                 [0x12] = "DDR",
289                 [0x13] = "DDR2",
290                 [0x14] = "DDR2 FB-DIMM",
291                 [0x15] = "Reserved",
292                 [0x16] = "Reserved",
293                 [0x17] = "Reserved",
294                 [0x18] = "DDR3",
295                 [0x19] = "FBD2",
296                 [0x1A] = "DDR4",
297                 [0x1B] = "LPDDR",
298                 [0x1C] = "LPDDR2",
299                 [0x1D] = "LPDDR3",
300                 [0x1E] = "LPDDR4",
301                 [0x1F] = "Logical non-volatile device",
302                 [0x20] = "HBM",
303                 [0x21] = "HBM2",
304         };
305 
306         printf("MEMORY_DEVICE_%u_TYPE=%s\n", slot_num,
307                code < ELEMENTSOF(type) && type[code] ? type[code] : OUT_OF_SPEC_STR);
308 }
309 
dmi_memory_device_type_detail(unsigned slot_num,uint16_t code)310 static void dmi_memory_device_type_detail(unsigned slot_num, uint16_t code) {
311         /* 7.18.3 */
312         static const char *detail[] = {
313                 [1]  = "Other",
314                 [2]  = "Unknown",
315                 [3]  = "Fast-paged",
316                 [4]  = "Static Column",
317                 [5]  = "Pseudo-static",
318                 [6]  = "RAMBus",
319                 [7]  = "Synchronous",
320                 [8]  = "CMOS",
321                 [9]  = "EDO",
322                 [10] = "Window DRAM",
323                 [11] = "Cache DRAM",
324                 [12] = "Non-Volatile",
325                 [13] = "Registered (Buffered)",
326                 [14] = "Unbuffered (Unregistered)",
327                 [15] = "LRDIMM",
328         };
329 
330         if ((code & 0xFFFE) == 0)
331                 printf("MEMORY_DEVICE_%u_TYPE_DETAIL=%s\n", slot_num, "None");
332         else {
333                 bool first_element = true;
334 
335                 printf("MEMORY_DEVICE_%u_TYPE_DETAIL=", slot_num);
336                 for (size_t i = 1; i < ELEMENTSOF(detail); i++)
337                         if (code & (1 << i)) {
338                                 printf("%s%s", first_element ? "" : " ", detail[i]);
339                                 first_element = false;
340                         }
341                 printf("\n");
342         }
343 }
344 
dmi_memory_device_speed(const char * attr_suffix,unsigned slot_num,uint16_t code)345 static void dmi_memory_device_speed(
346                 const char *attr_suffix,
347                 unsigned slot_num, uint16_t code) {
348         if (code != 0)
349                 printf("MEMORY_DEVICE_%u_%s=%u\n", slot_num, attr_suffix, code);
350 }
351 
dmi_memory_device_technology(unsigned slot_num,uint8_t code)352 static void dmi_memory_device_technology(unsigned slot_num, uint8_t code) {
353         /* 7.18.6 */
354         static const char * const technology[] = {
355                 [0x01] = "Other",
356                 [0x02] = "Unknown",
357                 [0x03] = "DRAM",
358                 [0x04] = "NVDIMM-N",
359                 [0x05] = "NVDIMM-F",
360                 [0x06] = "NVDIMM-P",
361                 [0x07] = "Intel Optane DC persistent memory",
362         };
363 
364         printf("MEMORY_DEVICE_%u_MEMORY_TECHNOLOGY=%s\n", slot_num,
365                code < ELEMENTSOF(technology) && technology[code] ? technology[code] : OUT_OF_SPEC_STR);
366 }
367 
dmi_memory_device_operating_mode_capability(unsigned slot_num,uint16_t code)368 static void dmi_memory_device_operating_mode_capability(unsigned slot_num, uint16_t code) {
369         /* 7.18.7 */
370         static const char * const mode[] = {
371                 [1] = "Other",
372                 [2] = "Unknown",
373                 [3] = "Volatile memory",
374                 [4] = "Byte-accessible persistent memory",
375                 [5] = "Block-accessible persistent memory",
376         };
377 
378         if ((code & 0xFFFE) != 0) {
379                 bool first_element = true;
380 
381                 printf("MEMORY_DEVICE_%u_MEMORY_OPERATING_MODE_CAPABILITY=", slot_num);
382                 for (size_t i = 1; i < ELEMENTSOF(mode); i++)
383                         if (code & (1 << i)) {
384                                 printf("%s%s", first_element ? "" : " ", mode[i]);
385                                 first_element = false;
386                         }
387                 printf("\n");
388         }
389 }
390 
dmi_memory_device_manufacturer_id(const char * attr_suffix,unsigned slot_num,uint16_t code)391 static void dmi_memory_device_manufacturer_id(
392                 const char *attr_suffix,
393                 unsigned slot_num, uint16_t code) {
394         /* 7.18.8 */
395         /* 7.18.10 */
396         /* LSB is 7-bit Odd Parity number of continuation codes */
397         if (code != 0)
398                 printf("MEMORY_DEVICE_%u_%s=Bank %d, Hex 0x%02X\n", slot_num, attr_suffix,
399                        (code & 0x7F) + 1, code >> 8);
400 }
401 
dmi_memory_device_product_id(const char * attr_suffix,unsigned slot_num,uint16_t code)402 static void dmi_memory_device_product_id(
403                 const char *attr_suffix,
404                 unsigned slot_num, uint16_t code) {
405         /* 7.18.9 */
406         /* 7.18.11 */
407         if (code != 0)
408                 printf("MEMORY_DEVICE_%u_%s=0x%04X\n", slot_num, attr_suffix, code);
409 }
410 
dmi_memory_device_size_detail(const char * attr_suffix,unsigned slot_num,uint64_t code)411 static void dmi_memory_device_size_detail(
412                 const char *attr_suffix,
413                 unsigned slot_num, uint64_t code) {
414         /* 7.18.12 */
415         /* 7.18.13 */
416         if (!IN_SET(code, 0x0LU, 0xFFFFFFFFFFFFFFFFLU))
417                 dmi_print_memory_size("MEMORY_DEVICE", attr_suffix, slot_num, code, MEMORY_SIZE_UNIT_BYTES);
418 }
419 
dmi_decode(const struct dmi_header * h,unsigned * next_slot_num)420 static void dmi_decode(const struct dmi_header *h,
421                        unsigned *next_slot_num) {
422         const uint8_t *data = h->data;
423         unsigned slot_num;
424 
425         /*
426          * Note: DMI types 37 and 42 are untested
427          */
428         switch (h->type) {
429         case 16: /* 7.17 Physical Memory Array */
430                 log_debug("Physical Memory Array");
431                 if (h->length < 0x0F)
432                         break;
433 
434                 if (data[0x05] != 0x03) /* 7.17.2, Use == "System Memory" */
435                         break;
436 
437                 log_debug("Use: System Memory");
438                 dmi_memory_array_location(data[0x04]);
439                 dmi_memory_array_ec_type(data[0x06]);
440                 if (DWORD(data + 0x07) != 0x80000000)
441                         dmi_print_memory_size("MEMORY_ARRAY", "MAX_CAPACITY", -1, DWORD(data + 0x07), MEMORY_SIZE_UNIT_KB);
442                 else if (h->length >= 0x17)
443                         dmi_print_memory_size("MEMORY_ARRAY", "MAX_CAPACITY", -1, QWORD(data + 0x0F), MEMORY_SIZE_UNIT_BYTES);
444 
445                 break;
446 
447         case 17: /* 7.18 Memory Device */
448                 slot_num = *next_slot_num;
449                 *next_slot_num = slot_num + 1;
450 
451                 log_debug("Memory Device: %u", slot_num);
452                 if (h->length < 0x15)
453                         break;
454 
455                 dmi_memory_device_width("TOTAL_WIDTH", slot_num, WORD(data + 0x08));
456                 dmi_memory_device_width("DATA_WIDTH", slot_num, WORD(data + 0x0A));
457                 if (h->length >= 0x20 && WORD(data + 0x0C) == 0x7FFF)
458                         dmi_memory_device_extended_size(slot_num, DWORD(data + 0x1C));
459                 else
460                         dmi_memory_device_size(slot_num, WORD(data + 0x0C));
461                 dmi_memory_device_form_factor(slot_num, data[0x0E]);
462                 dmi_memory_device_set(slot_num, data[0x0F]);
463                 dmi_memory_device_string("LOCATOR", slot_num, h, data[0x10]);
464                 dmi_memory_device_string("BANK_LOCATOR", slot_num, h, data[0x11]);
465                 dmi_memory_device_type(slot_num, data[0x12]);
466                 dmi_memory_device_type_detail(slot_num, WORD(data + 0x13));
467                 if (h->length < 0x17)
468                         break;
469 
470                 dmi_memory_device_speed("SPEED_MTS", slot_num, WORD(data + 0x15));
471                 if (h->length < 0x1B)
472                         break;
473 
474                 dmi_memory_device_string("MANUFACTURER", slot_num, h, data[0x17]);
475                 dmi_memory_device_string("SERIAL_NUMBER", slot_num, h, data[0x18]);
476                 dmi_memory_device_string("ASSET_TAG", slot_num, h, data[0x19]);
477                 dmi_memory_device_string("PART_NUMBER", slot_num, h, data[0x1A]);
478                 if (h->length < 0x1C)
479                         break;
480 
481                 dmi_memory_device_rank(slot_num, data[0x1B]);
482                 if (h->length < 0x22)
483                         break;
484 
485                 dmi_memory_device_speed("CONFIGURED_SPEED_MTS", slot_num, WORD(data + 0x20));
486                 if (h->length < 0x28)
487                         break;
488 
489                 dmi_memory_device_voltage_value("MINIMUM_VOLTAGE", slot_num, WORD(data + 0x22));
490                 dmi_memory_device_voltage_value("MAXIMUM_VOLTAGE", slot_num, WORD(data + 0x24));
491                 dmi_memory_device_voltage_value("CONFIGURED_VOLTAGE", slot_num, WORD(data + 0x26));
492                 if (h->length < 0x34)
493                         break;
494 
495                 dmi_memory_device_technology(slot_num, data[0x28]);
496                 dmi_memory_device_operating_mode_capability(slot_num, WORD(data + 0x29));
497                 dmi_memory_device_string("FIRMWARE_VERSION", slot_num, h, data[0x2B]);
498                 dmi_memory_device_manufacturer_id("MODULE_MANUFACTURER_ID", slot_num, WORD(data + 0x2C));
499                 dmi_memory_device_product_id("MODULE_PRODUCT_ID", slot_num, WORD(data + 0x2E));
500                 dmi_memory_device_manufacturer_id("MEMORY_SUBSYSTEM_CONTROLLER_MANUFACTURER_ID",
501                                                   slot_num, WORD(data + 0x30));
502                 dmi_memory_device_product_id("MEMORY_SUBSYSTEM_CONTROLLER_PRODUCT_ID",
503                                              slot_num, WORD(data + 0x32));
504                 if (h->length < 0x3C)
505                         break;
506 
507                 dmi_memory_device_size_detail("NON_VOLATILE_SIZE", slot_num, QWORD(data + 0x34));
508                 if (h->length < 0x44)
509                         break;
510 
511                 dmi_memory_device_size_detail("VOLATILE_SIZE", slot_num, QWORD(data + 0x3C));
512                 if (h->length < 0x4C)
513                         break;
514 
515                 dmi_memory_device_size_detail("CACHE_SIZE", slot_num, QWORD(data + 0x44));
516                 if (h->length < 0x54)
517                         break;
518 
519                 dmi_memory_device_size_detail("LOGICAL_SIZE", slot_num, QWORD(data + 0x4C));
520 
521                 break;
522         }
523 }
524 
dmi_table_decode(const uint8_t * buf,size_t len,uint16_t num)525 static void dmi_table_decode(const uint8_t *buf, size_t len, uint16_t num) {
526         const uint8_t *data = buf;
527         unsigned next_slot_num = 0;
528 
529         /* 4 is the length of an SMBIOS structure header */
530         for (uint16_t i = 0; (i < num || num == 0) && data + 4 <= buf + len; i++) {
531                 struct dmi_header h = (struct dmi_header) {
532                         .type = data[0],
533                         .length = data[1],
534                         .handle = WORD(data + 2),
535                         .data = data,
536                 };
537                 bool display = !IN_SET(h.type, 126, 127);
538                 const uint8_t *next;
539 
540                 /* If a short entry is found (less than 4 bytes), not only it
541                  * is invalid, but we cannot reliably locate the next entry.
542                  * Better stop at this point, and let the user know their
543                  * table is broken. */
544                 if (h.length < 4)
545                         break;
546 
547                 /* In quiet mode, stop decoding at end of table marker */
548                 if (h.type == 127)
549                         break;
550 
551                 /* Look for the next handle */
552                 next = data + h.length;
553                 while ((size_t)(next - buf + 1) < len && (next[0] != 0 || next[1] != 0))
554                         next++;
555                 next += 2;
556 
557                 /* Make sure the whole structure fits in the table */
558                 if ((size_t)(next - buf) > len)
559                         break;
560 
561                 if (display)
562                         dmi_decode(&h, &next_slot_num);
563 
564                 data = next;
565         }
566         if (next_slot_num > 0)
567                 printf("MEMORY_ARRAY_NUM_DEVICES=%u\n", next_slot_num);
568 }
569 
dmi_table(int64_t base,uint32_t len,uint16_t num,const char * devmem,bool no_file_offset)570 static int dmi_table(int64_t base, uint32_t len, uint16_t num, const char *devmem, bool no_file_offset) {
571         _cleanup_free_ uint8_t *buf = NULL;
572         size_t size;
573         int r;
574 
575         /*
576          * When reading from sysfs or from a dump file, the file may be
577          * shorter than announced. For SMBIOS v3 this is expected, as we
578          * only know the maximum table size, not the actual table size.
579          * For older implementations (and for SMBIOS v3 too), this
580          * would be the result of the kernel truncating the table on
581          * parse error.
582          */
583         r = read_full_file_full(AT_FDCWD, devmem, no_file_offset ? 0 : base, len,
584                                 0, NULL, (char **) &buf, &size);
585         if (r < 0)
586                 return log_error_errno(r, "Failed to read table: %m");
587 
588         dmi_table_decode(buf, size, num);
589 
590         return 0;
591 }
592 
593 /* Same thing for SMBIOS3 entry points */
smbios3_decode(const uint8_t * buf,const char * devmem,bool no_file_offset)594 static int smbios3_decode(const uint8_t *buf, const char *devmem, bool no_file_offset) {
595         uint64_t offset;
596 
597         /* Don't let checksum run beyond the buffer */
598         if (buf[0x06] > 0x20)
599                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
600                                        "Entry point length too large (%"PRIu8" bytes, expected %u).",
601                                        buf[0x06], 0x18U);
602 
603         if (!verify_checksum(buf, buf[0x06]))
604                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to verify checksum.");
605 
606         offset = QWORD(buf + 0x10);
607 
608 #if __SIZEOF_SIZE_T__ != 8
609         if (!no_file_offset && (offset >> 32) != 0)
610                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "64-bit addresses not supported on 32-bit systems.");
611 #endif
612 
613         return dmi_table(offset, DWORD(buf + 0x0C), 0, devmem, no_file_offset);
614 }
615 
smbios_decode(const uint8_t * buf,const char * devmem,bool no_file_offset)616 static int smbios_decode(const uint8_t *buf, const char *devmem, bool no_file_offset) {
617         /* Don't let checksum run beyond the buffer */
618         if (buf[0x05] > 0x20)
619                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
620                                        "Entry point length too large (%"PRIu8" bytes, expected %u).",
621                                        buf[0x05], 0x1FU);
622 
623         if (!verify_checksum(buf, buf[0x05])
624             || memcmp(buf + 0x10, "_DMI_", 5) != 0
625             || !verify_checksum(buf + 0x10, 0x0F))
626                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to verify checksum.");
627 
628         return dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C),
629                          devmem, no_file_offset);
630 }
631 
legacy_decode(const uint8_t * buf,const char * devmem,bool no_file_offset)632 static int legacy_decode(const uint8_t *buf, const char *devmem, bool no_file_offset) {
633         if (!verify_checksum(buf, 0x0F))
634                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to verify checksum.");
635 
636         return dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), WORD(buf + 0x0C),
637                          devmem, no_file_offset);
638 }
639 
help(void)640 static int help(void) {
641         printf("Usage: %s [options]\n"
642                " -F,--from-dump FILE   read DMI information from a binary file\n"
643                " -h,--help             print this help text\n\n",
644                program_invocation_short_name);
645         return 0;
646 }
647 
parse_argv(int argc,char * const * argv)648 static int parse_argv(int argc, char * const *argv) {
649         static const struct option options[] = {
650                 { "from-dump", required_argument, NULL, 'F' },
651                 { "version",   no_argument,       NULL, 'V' },
652                 { "help",      no_argument,       NULL, 'h' },
653                 {}
654         };
655         int c;
656 
657         while ((c = getopt_long(argc, argv, "F:hV", options, NULL)) >= 0)
658                 switch (c) {
659                 case 'F':
660                         arg_source_file = optarg;
661                         break;
662                 case 'V':
663                         printf("%s\n", GIT_VERSION);
664                         return 0;
665                 case 'h':
666                         return help();
667                 case '?':
668                         return -EINVAL;
669                 default:
670                         assert_not_reached();
671                 }
672 
673         return 1;
674 }
675 
run(int argc,char * const * argv)676 static int run(int argc, char* const* argv) {
677         _cleanup_free_ uint8_t *buf = NULL;
678         bool no_file_offset = false;
679         size_t size;
680         int r;
681 
682         log_set_target(LOG_TARGET_AUTO);
683         udev_parse_config();
684         log_parse_environment();
685         log_open();
686 
687         r = parse_argv(argc, argv);
688         if (r <= 0)
689                 return r;
690 
691         /* Read from dump if so instructed */
692         r = read_full_file_full(AT_FDCWD,
693                                 arg_source_file ?: SYS_ENTRY_FILE,
694                                 0, 0x20, 0, NULL, (char **) &buf, &size);
695         if (r < 0)
696                 return log_full_errno(!arg_source_file && r == -ENOENT ? LOG_DEBUG : LOG_ERR,
697                                       r, "Reading \"%s\" failed: %m",
698                                       arg_source_file ?: SYS_ENTRY_FILE);
699 
700         if (!arg_source_file) {
701                 arg_source_file = SYS_TABLE_FILE;
702                 no_file_offset = true;
703         }
704 
705         if (size >= 24 && memory_startswith(buf, size, "_SM3_"))
706                 return smbios3_decode(buf, arg_source_file, no_file_offset);
707         if (size >= 31 && memory_startswith(buf, size, "_SM_"))
708                 return smbios_decode(buf, arg_source_file, no_file_offset);
709         if (size >= 15 && memory_startswith(buf, size, "_DMI_"))
710                 return legacy_decode(buf, arg_source_file, no_file_offset);
711 
712         return -EINVAL;
713 }
714 
715 DEFINE_MAIN_FUNCTION(run);
716