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