1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * System Memory information
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>
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.
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.
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
47 #include "alloc-util.h"
49 #include "main-func.h"
50 #include "string-util.h"
51 #include "udev-util.h"
52 #include "unaligned.h"
55 #define SUPPORTED_SMBIOS_VER 0x030300
57 #define OUT_OF_SPEC_STR "<OUT OF SPEC>"
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"
64 * Per SMBIOS v2.8.0 and later, all structures assume a little-endian
65 * ordering convention.
67 #define WORD(x) (unaligned_read_le16(x))
68 #define DWORD(x) (unaligned_read_le32(x))
69 #define QWORD(x) (unaligned_read_le64(x))
78 static const char *arg_source_file
= NULL
;
80 static bool verify_checksum(const uint8_t *buf
, size_t len
) {
83 for (size_t a
= 0; a
< len
; a
++)
89 * Type-independant Stuff
92 static const char *dmi_string(const struct dmi_header
*dm
, uint8_t s
) {
93 const char *bp
= (const char *) dm
->data
;
96 return "Not Specified";
99 for (;s
> 1 && !isempty(bp
); s
--)
100 bp
+= strlen(bp
) + 1;
103 return "<BAD INDEX>";
109 MEMORY_SIZE_UNIT_BYTES
,
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
)
120 printf("%s_%u_%s=%"PRIu64
"\n", attr_prefix
, slot_num
, attr_suffix
, code
);
122 printf("%s_%s=%"PRIu64
"\n", attr_prefix
, attr_suffix
, code
);
126 * 7.17 Physical Memory Array (Type 16)
129 static void dmi_memory_array_location(uint8_t code
) {
131 static const char *location
[] = {
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",
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 */
150 const char *str
= OUT_OF_SPEC_STR
;
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];
157 printf("MEMORY_ARRAY_LOCATION=%s\n", str
);
160 static void dmi_memory_array_ec_type(uint8_t code
) {
162 static const char *type
[] = {
167 [0x05] = "Single-bit ECC",
168 [0x06] = "Multi-bit ECC",
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
);
178 * 7.18 Memory Device (Type 17)
181 static void dmi_memory_device_string(
182 const char *attr_suffix
, unsigned slot_num
,
183 const struct dmi_header
*h
, uint8_t s
) {
186 str
= strdupa(dmi_string(h
, s
));
189 printf("MEMORY_DEVICE_%u_%s=%s\n", slot_num
, attr_suffix
, str
);
192 static void dmi_memory_device_width(
193 const char *attr_suffix
,
194 unsigned slot_num
, uint16_t code
) {
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
);
201 static void dmi_memory_device_size(unsigned slot_num
, uint16_t code
) {
203 return (void) printf("MEMORY_DEVICE_%u_PRESENT=0\n", slot_num
);
207 uint64_t s
= code
& 0x7FFF;
208 if (!(code
& 0x8000))
210 dmi_print_memory_size("MEMORY_DEVICE", "SIZE", slot_num
, s
, MEMORY_SIZE_UNIT_KB
);
213 static void dmi_memory_device_extended_size(unsigned slot_num
, uint32_t code
) {
214 uint64_t capacity
= (uint64_t) code
* 1024 * 1024;
216 printf("MEMORY_DEVICE_%u_SIZE=%"PRIu64
"\n", slot_num
, capacity
);
219 static void dmi_memory_device_rank(unsigned slot_num
, uint8_t code
) {
222 printf("MEMORY_DEVICE_%u_RANK=%u\n", slot_num
, code
);
225 static void dmi_memory_device_voltage_value(
226 const char *attr_suffix
,
227 unsigned slot_num
, uint16_t code
) {
231 printf("MEMORY_DEVICE_%u_%s=%g\n", slot_num
, attr_suffix
, (double)code
/ 1000);
233 printf("MEMORY_DEVICE_%u_%s=%.1g\n", slot_num
, attr_suffix
, (double)code
/ 1000);
236 static void dmi_memory_device_form_factor(unsigned slot_num
, uint8_t code
) {
238 static const char *form_factor
[] = {
246 [0x08] = "Proprietary Card",
249 [0x0B] = "Row Of Chips",
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
);
261 static void dmi_memory_device_set(unsigned slot_num
, uint8_t code
) {
263 printf("MEMORY_DEVICE_%u_SET=%s\n", slot_num
, "Unknown");
265 printf("MEMORY_DEVICE_%u_SET=%"PRIu8
"\n", slot_num
, code
);
268 static void dmi_memory_device_type(unsigned slot_num
, uint8_t code
) {
270 static const char *type
[] = {
290 [0x14] = "DDR2 FB-DIMM",
301 [0x1F] = "Logical non-volatile device",
306 printf("MEMORY_DEVICE_%u_TYPE=%s\n", slot_num
,
307 code
< ELEMENTSOF(type
) && type
[code
] ? type
[code
] : OUT_OF_SPEC_STR
);
310 static void dmi_memory_device_type_detail(unsigned slot_num
, uint16_t code
) {
312 static const char *detail
[] = {
316 [4] = "Static Column",
317 [5] = "Pseudo-static",
322 [10] = "Window DRAM",
324 [12] = "Non-Volatile",
325 [13] = "Registered (Buffered)",
326 [14] = "Unbuffered (Unregistered)",
330 if ((code
& 0xFFFE) == 0)
331 printf("MEMORY_DEVICE_%u_TYPE_DETAIL=%s\n", slot_num
, "None");
333 bool first_element
= true;
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;
345 static void dmi_memory_device_speed(
346 const char *attr_suffix
,
347 unsigned slot_num
, uint16_t code
) {
349 printf("MEMORY_DEVICE_%u_%s=%u\n", slot_num
, attr_suffix
, code
);
352 static void dmi_memory_device_technology(unsigned slot_num
, uint8_t code
) {
354 static const char * const technology
[] = {
361 [0x07] = "Intel Optane DC persistent memory",
364 printf("MEMORY_DEVICE_%u_MEMORY_TECHNOLOGY=%s\n", slot_num
,
365 code
< ELEMENTSOF(technology
) && technology
[code
] ? technology
[code
] : OUT_OF_SPEC_STR
);
368 static void dmi_memory_device_operating_mode_capability(unsigned slot_num
, uint16_t code
) {
370 static const char * const mode
[] = {
373 [3] = "Volatile memory",
374 [4] = "Byte-accessible persistent memory",
375 [5] = "Block-accessible persistent memory",
378 if ((code
& 0xFFFE) != 0) {
379 bool first_element
= true;
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;
391 static void dmi_memory_device_manufacturer_id(
392 const char *attr_suffix
,
393 unsigned slot_num
, uint16_t code
) {
396 /* LSB is 7-bit Odd Parity number of continuation codes */
398 printf("MEMORY_DEVICE_%u_%s=Bank %d, Hex 0x%02X\n", slot_num
, attr_suffix
,
399 (code
& 0x7F) + 1, code
>> 8);
402 static void dmi_memory_device_product_id(
403 const char *attr_suffix
,
404 unsigned slot_num
, uint16_t code
) {
408 printf("MEMORY_DEVICE_%u_%s=0x%04X\n", slot_num
, attr_suffix
, code
);
411 static void dmi_memory_device_size_detail(
412 const char *attr_suffix
,
413 unsigned slot_num
, uint64_t code
) {
416 if (!IN_SET(code
, 0x0LU
, 0xFFFFFFFFFFFFFFFFLU
))
417 dmi_print_memory_size("MEMORY_DEVICE", attr_suffix
, slot_num
, code
, MEMORY_SIZE_UNIT_BYTES
);
420 static void dmi_decode(const struct dmi_header
*h
) {
421 const uint8_t *data
= h
->data
;
422 static unsigned next_slot_num
= 0;
426 * Note: DMI types 37 and 42 are untested
429 case 16: /* 7.17 Physical Memory Array */
430 log_debug("Physical Memory Array");
431 if (h
->length
< 0x0F)
434 if (data
[0x05] != 0x03) /* 7.17.2, Use == "System Memory" */
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 printf("MEMORY_ARRAY_NUM_DEVICES=%u\n", WORD(data
+ 0x0D));
448 case 17: /* 7.18 Memory Device */
449 slot_num
= next_slot_num
;
452 log_debug("Memory Device");
453 if (h
->length
< 0x15)
456 dmi_memory_device_width("TOTAL_WIDTH", slot_num
, WORD(data
+ 0x08));
457 dmi_memory_device_width("DATA_WIDTH", slot_num
, WORD(data
+ 0x0A));
458 if (h
->length
>= 0x20 && WORD(data
+ 0x0C) == 0x7FFF)
459 dmi_memory_device_extended_size(slot_num
, DWORD(data
+ 0x1C));
461 dmi_memory_device_size(slot_num
, WORD(data
+ 0x0C));
462 dmi_memory_device_form_factor(slot_num
, data
[0x0E]);
463 dmi_memory_device_set(slot_num
, data
[0x0F]);
464 dmi_memory_device_string("LOCATOR", slot_num
, h
, data
[0x10]);
465 dmi_memory_device_string("BANK_LOCATOR", slot_num
, h
, data
[0x11]);
466 dmi_memory_device_type(slot_num
, data
[0x12]);
467 dmi_memory_device_type_detail(slot_num
, WORD(data
+ 0x13));
468 if (h
->length
< 0x17)
471 dmi_memory_device_speed("SPEED_MTS", slot_num
, WORD(data
+ 0x15));
472 if (h
->length
< 0x1B)
475 dmi_memory_device_string("MANUFACTURER", slot_num
, h
, data
[0x17]);
476 dmi_memory_device_string("SERIAL_NUMBER", slot_num
, h
, data
[0x18]);
477 dmi_memory_device_string("ASSET_TAG", slot_num
, h
, data
[0x19]);
478 dmi_memory_device_string("PART_NUMBER", slot_num
, h
, data
[0x1A]);
479 if (h
->length
< 0x1C)
482 dmi_memory_device_rank(slot_num
, data
[0x1B]);
483 if (h
->length
< 0x22)
486 dmi_memory_device_speed("CONFIGURED_SPEED_MTS", slot_num
, WORD(data
+ 0x20));
487 if (h
->length
< 0x28)
490 dmi_memory_device_voltage_value("MINIMUM_VOLTAGE", slot_num
, WORD(data
+ 0x22));
491 dmi_memory_device_voltage_value("MAXIMUM_VOLTAGE", slot_num
, WORD(data
+ 0x24));
492 dmi_memory_device_voltage_value("CONFIGURED_VOLTAGE", slot_num
, WORD(data
+ 0x26));
493 if (h
->length
< 0x34)
496 dmi_memory_device_technology(slot_num
, data
[0x28]);
497 dmi_memory_device_operating_mode_capability(slot_num
, WORD(data
+ 0x29));
498 dmi_memory_device_string("FIRMWARE_VERSION", slot_num
, h
, data
[0x2B]);
499 dmi_memory_device_manufacturer_id("MODULE_MANUFACTURER_ID", slot_num
, WORD(data
+ 0x2C));
500 dmi_memory_device_product_id("MODULE_PRODUCT_ID", slot_num
, WORD(data
+ 0x2E));
501 dmi_memory_device_manufacturer_id("MEMORY_SUBSYSTEM_CONTROLLER_MANUFACTURER_ID",
502 slot_num
, WORD(data
+ 0x30));
503 dmi_memory_device_product_id("MEMORY_SUBSYSTEM_CONTROLLER_PRODUCT_ID",
504 slot_num
, WORD(data
+ 0x32));
505 if (h
->length
< 0x3C)
508 dmi_memory_device_size_detail("NON_VOLATILE_SIZE", slot_num
, QWORD(data
+ 0x34));
509 if (h
->length
< 0x44)
512 dmi_memory_device_size_detail("VOLATILE_SIZE", slot_num
, QWORD(data
+ 0x3C));
513 if (h
->length
< 0x4C)
516 dmi_memory_device_size_detail("CACHE_SIZE", slot_num
, QWORD(data
+ 0x44));
517 if (h
->length
< 0x54)
520 dmi_memory_device_size_detail("LOGICAL_SIZE", slot_num
, QWORD(data
+ 0x4C));
526 static void dmi_table_decode(const uint8_t *buf
, size_t len
, uint16_t num
) {
527 const uint8_t *data
= buf
;
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
) {
534 .handle
= WORD(data
+ 2),
537 bool display
= !IN_SET(h
.type
, 126, 127);
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 his/her
543 * table is broken. */
547 /* In quiet mode, stop decoding at end of table marker */
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))
557 /* Make sure the whole structure fits in the table */
558 if ((size_t)(next
- buf
) > len
)
568 static int dmi_table(int64_t base
, uint32_t len
, uint16_t num
, const char *devmem
, bool no_file_offset
) {
569 _cleanup_free_
uint8_t *buf
= NULL
;
574 * When reading from sysfs or from a dump file, the file may be
575 * shorter than announced. For SMBIOS v3 this is expected, as we
576 * only know the maximum table size, not the actual table size.
577 * For older implementations (and for SMBIOS v3 too), this
578 * would be the result of the kernel truncating the table on
581 r
= read_full_file_full(AT_FDCWD
, devmem
, no_file_offset
? 0 : base
, len
,
582 0, NULL
, (char **) &buf
, &size
);
584 return log_error_errno(r
, "Failed to read table: %m");
586 dmi_table_decode(buf
, size
, num
);
591 /* Same thing for SMBIOS3 entry points */
592 static int smbios3_decode(const uint8_t *buf
, const char *devmem
, bool no_file_offset
) {
595 /* Don't let checksum run beyond the buffer */
596 if (buf
[0x06] > 0x20)
597 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
598 "Entry point length too large (%"PRIu8
" bytes, expected %u).",
601 if (!verify_checksum(buf
, buf
[0x06]))
602 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to verify checksum.");
604 offset
= QWORD(buf
+ 0x10);
606 #if __SIZEOF_SIZE_T__ != 8
607 if (!no_file_offset
&& (offset
>> 32) != 0)
608 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "64-bit addresses not supported on 32-bit systems.");
611 return dmi_table(offset
, DWORD(buf
+ 0x0C), 0, devmem
, no_file_offset
);
614 static int smbios_decode(const uint8_t *buf
, const char *devmem
, bool no_file_offset
) {
615 /* Don't let checksum run beyond the buffer */
616 if (buf
[0x05] > 0x20)
617 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
618 "Entry point length too large (%"PRIu8
" bytes, expected %u).",
621 if (!verify_checksum(buf
, buf
[0x05])
622 || memcmp(buf
+ 0x10, "_DMI_", 5) != 0
623 || !verify_checksum(buf
+ 0x10, 0x0F))
624 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to verify checksum.");
626 return dmi_table(DWORD(buf
+ 0x18), WORD(buf
+ 0x16), WORD(buf
+ 0x1C),
627 devmem
, no_file_offset
);
630 static int legacy_decode(const uint8_t *buf
, const char *devmem
, bool no_file_offset
) {
631 if (!verify_checksum(buf
, 0x0F))
632 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Failed to verify checksum.");
634 return dmi_table(DWORD(buf
+ 0x08), WORD(buf
+ 0x06), WORD(buf
+ 0x0C),
635 devmem
, no_file_offset
);
638 static int help(void) {
639 printf("Usage: %s [options]\n"
640 " -F,--from-dump FILE read DMI information from a binary file\n"
641 " -h,--help print this help text\n\n",
642 program_invocation_short_name
);
646 static int parse_argv(int argc
, char * const *argv
) {
647 static const struct option options
[] = {
648 { "from-dump", required_argument
, NULL
, 'F' },
649 { "version", no_argument
, NULL
, 'V' },
650 { "help", no_argument
, NULL
, 'h' },
655 while ((c
= getopt_long(argc
, argv
, "F:hV", options
, NULL
)) >= 0)
658 arg_source_file
= optarg
;
661 printf("%s\n", GIT_VERSION
);
668 assert_not_reached("Unknown option");
674 static int run(int argc
, char* const* argv
) {
675 _cleanup_free_
uint8_t *buf
= NULL
;
676 bool no_file_offset
= false;
680 log_set_target(LOG_TARGET_AUTO
);
682 log_parse_environment();
685 r
= parse_argv(argc
, argv
);
689 /* Read from dump if so instructed */
690 r
= read_full_file_full(AT_FDCWD
,
691 arg_source_file
?: SYS_ENTRY_FILE
,
692 0, 0x20, 0, NULL
, (char **) &buf
, &size
);
694 return log_full_errno(!arg_source_file
&& r
== -ENOENT
? LOG_DEBUG
: LOG_ERR
,
695 r
, "Reading \"%s\" failed: %m",
696 arg_source_file
?: SYS_ENTRY_FILE
);
698 if (!arg_source_file
) {
699 arg_source_file
= SYS_TABLE_FILE
;
700 no_file_offset
= true;
703 if (size
>= 24 && memory_startswith(buf
, size
, "_SM3_"))
704 return smbios3_decode(buf
, arg_source_file
, no_file_offset
);
705 if (size
>= 31 && memory_startswith(buf
, size
, "_SM_"))
706 return smbios_decode(buf
, arg_source_file
, no_file_offset
);
707 if (size
>= 15 && memory_startswith(buf
, size
, "_DMI_"))
708 return legacy_decode(buf
, arg_source_file
, no_file_offset
);
713 DEFINE_MAIN_FUNCTION(run
);