[PATCH v4 4/5] smbios: add support for dynamic generation of Type 17 table

Raymond Mao raymondmaoca at gmail.com
Fri Nov 14 18:06:01 CET 2025


This commit implements SMBIOS Type 17 (Memory Device) generation with a
hybrid approach supporting both:

1. Explicit definition via Device Tree 'smbios' node:
   Child node under '/smbios/smbios/memory-device' will be used to
   populate as individual Type 17 structure directly.
   - Properties follow SMBIOS field names with lowercase letters and
     hyphen-separated words (e.g., 'physical-memory-array-handle',
     ' memory-error-information-handle', 'configured-memory-speed', etc.).
   - This method supports precise platform-defined overrides and system
     descriptions.

2. Fallback to automatic DT-based discovery:
   If child node under '/smbios/smbios/memory-device' does not exist,
   the implementation will:
   - Scan all top-level 'memory@' nodes to populate Type 17 structure with
     inferred size and location data.
   - Scan nodes named or marked as 'memory-controller' and parse
     associated 'dimm@' subnodes (if present) to extract DIMM sizes and
     map them accordingly.

This dual-mode support enables flexible firmware SMBIOS reporting while
aligning with spec-compliant naming and runtime-detected memory topology.

Type 17 support is under GENERATE_SMBIOS_TABLE_VERBOSE to avoid
increasing rom size for those platforms which only require basic SMBIOS
support.

Signed-off-by: Raymond Mao <raymondmaoca at gmail.com>
---
Changes in v4:
- Initial patch.

 arch/arm/dts/smbios_generic.dtsi |   3 +
 cmd/smbios.c                     | 126 +++++++++++
 include/smbios.h                 |  45 ++++
 include/smbios_def.h             | 127 +++++++++++
 lib/smbios.c                     | 377 +++++++++++++++++++++++++++++++
 5 files changed, 678 insertions(+)

diff --git a/arch/arm/dts/smbios_generic.dtsi b/arch/arm/dts/smbios_generic.dtsi
index 1a9adfaa409..fd2df8d02e0 100644
--- a/arch/arm/dts/smbios_generic.dtsi
+++ b/arch/arm/dts/smbios_generic.dtsi
@@ -83,6 +83,9 @@
 
 			memory-array {
 			};
+
+			memory-device {
+			};
 		};
 	};
 };
diff --git a/cmd/smbios.c b/cmd/smbios.c
index 3f7dd21f92e..39c9c44a28e 100644
--- a/cmd/smbios.c
+++ b/cmd/smbios.c
@@ -194,6 +194,74 @@ static const struct str_lookup_table ma_err_corr_strings[] = {
 	{ SMBIOS_MA_ERRCORR_CRC,	"CRC" },
 };
 
+static const struct str_lookup_table md_form_factor_strings[] = {
+	{ SMBIOS_MD_FF_OTHER,		"Other" },
+	{ SMBIOS_MD_FF_UNKNOWN,		"Unknown" },
+	{ SMBIOS_MD_FF_SIMM,		"SIMM" },
+	{ SMBIOS_MD_FF_SIP,		"SIP" },
+	{ SMBIOS_MD_FF_CHIP,		"Chip" },
+	{ SMBIOS_MD_FF_DIP,		"DIP" },
+	{ SMBIOS_MD_FF_ZIP,		"ZIP" },
+	{ SMBIOS_MD_FF_PROPCARD,	"Proprietary Card" },
+	{ SMBIOS_MD_FF_DIMM,		"DIMM" },
+	{ SMBIOS_MD_FF_TSOP,		"TSOP" },
+	{ SMBIOS_MD_FF_ROC,		"Row of chips" },
+	{ SMBIOS_MD_FF_RIMM,		"RIMM" },
+	{ SMBIOS_MD_FF_SODIMM,		"SODIMM" },
+	{ SMBIOS_MD_FF_SRIMM,		"SRIMM" },
+	{ SMBIOS_MD_FF_FBDIMM,		"FB-DIMM" },
+	{ SMBIOS_MD_FF_DIE,		"Die" },
+};
+
+static const struct str_lookup_table md_type_strings[] = {
+	{ SMBIOS_MD_TYPE_OTHER,		"Other" },
+	{ SMBIOS_MD_TYPE_UNKNOWN,	"Unknown" },
+	{ SMBIOS_MD_TYPE_DRAM,		"DRAM" },
+	{ SMBIOS_MD_TYPE_EDRAM,		"EDRAM" },
+	{ SMBIOS_MD_TYPE_VRAM,		"VRAM" },
+	{ SMBIOS_MD_TYPE_SRAM,		"SRAM" },
+	{ SMBIOS_MD_TYPE_RAM,		"RAM" },
+	{ SMBIOS_MD_TYPE_ROM,		"ROM" },
+	{ SMBIOS_MD_TYPE_FLASH,		"FLASH" },
+	{ SMBIOS_MD_TYPE_EEPROM,	"EEPROM" },
+	{ SMBIOS_MD_TYPE_FEPROM,	"FEPROM" },
+	{ SMBIOS_MD_TYPE_EPROM,		"EPROM" },
+	{ SMBIOS_MD_TYPE_CDRAM,		"CDRAM" },
+	{ SMBIOS_MD_TYPE_3DRAM,		"3DRAM" },
+	{ SMBIOS_MD_TYPE_SDRAM,		"SDRAM" },
+	{ SMBIOS_MD_TYPE_SGRAM,		"SGRAM" },
+	{ SMBIOS_MD_TYPE_RDRAM,		"RDRAM" },
+	{ SMBIOS_MD_TYPE_DDR,		"DDR" },
+	{ SMBIOS_MD_TYPE_DDR2,		"DDR2" },
+	{ SMBIOS_MD_TYPE_DDR2FBD,	"DDR2 FB-DIMM" },
+	{ SMBIOS_MD_TYPE_RSVD1,		"Reserved" },
+	{ SMBIOS_MD_TYPE_RSVD2,		"Reserved" },
+	{ SMBIOS_MD_TYPE_DSVD3,		"Reserved" },
+	{ SMBIOS_MD_TYPE_DDR3,		"DDR3" },
+	{ SMBIOS_MD_TYPE_FBD2,		"FBD2" },
+	{ SMBIOS_MD_TYPE_DDR4,		"DDR4" },
+	{ SMBIOS_MD_TYPE_LPDDR,		"LPDDR" },
+	{ SMBIOS_MD_TYPE_LPDDR2,	"LPDDR2" },
+	{ SMBIOS_MD_TYPE_LPDDR3,	"LPDDR3" },
+	{ SMBIOS_MD_TYPE_LPDDR4,	"LPDDR4" },
+	{ SMBIOS_MD_TYPE_LNVD,		"Logical non-volatile device" },
+	{ SMBIOS_MD_TYPE_HBM,		"HBM" },
+	{ SMBIOS_MD_TYPE_HBM2,		"HBM2" },
+	{ SMBIOS_MD_TYPE_DDR5,		"DDR5" },
+	{ SMBIOS_MD_TYPE_LPDDR5,	"LPDDR5" },
+	{ SMBIOS_MD_TYPE_HBM3,		"HBM3" },
+};
+
+static const struct str_lookup_table md_tech_strings[] = {
+	{ SMBIOS_MD_TECH_OTHER,		"Other" },
+	{ SMBIOS_MD_TECH_UNKNOWN,	"Unknown" },
+	{ SMBIOS_MD_TECH_DRAM,		"DRAM" },
+	{ SMBIOS_MD_TECH_NVDIMMN,	"NVDIMM-N" },
+	{ SMBIOS_MD_TECH_NVDIMMF,	"NVDIMM-F" },
+	{ SMBIOS_MD_TECH_NVDIMMP,	"NVDIMM-P" },
+	{ SMBIOS_MD_TECH_OPTANE,	"Intel Optane persistent memory" },
+};
+
 /**
  * smbios_get_string() - get SMBIOS string from table
  *
@@ -557,6 +625,61 @@ static void smbios_print_type16(struct smbios_type16 *table)
 	printf("\tExtended Maximum Capacity: 0x%016llx\n", table->ext_max_cap);
 }
 
+static void smbios_print_type17(struct smbios_type17 *table)
+{
+	printf("Memory Device:\n");
+	printf("\tPhysical Memory Array Handle: 0x%04x\n",
+	       table->phy_mem_array_hdl);
+	printf("\tMemory Error Information Handle: 0x%04x\n",
+	       table->mem_err_info_hdl);
+	printf("\tTotal Width: 0x%04x\n", table->total_width);
+	printf("\tData Width: 0x%04x\n", table->data_width);
+	printf("\tSize: 0x%04x\n", table->size);
+	smbios_print_lookup_str(md_form_factor_strings, table->form_factor,
+				ARRAY_SIZE(md_form_factor_strings),
+				"Form Factor");
+	printf("\tDevice Set: 0x%04x\n", table->dev_set);
+	smbios_print_str("Device Locator", table, table->dev_locator);
+	smbios_print_str("Bank Locator", table, table->bank_locator);
+	smbios_print_lookup_str(md_type_strings, table->mem_type,
+				ARRAY_SIZE(md_type_strings), "Memory Type");
+	printf("\tType Detail: 0x%04x\n", table->type_detail);
+	printf("\tSpeed: 0x%04x\n", table->speed);
+	smbios_print_str("Manufacturer", table, table->manufacturer);
+	smbios_print_str("Serial Number", table, table->serial_number);
+	smbios_print_str("Asset Tag", table, table->asset_tag);
+	smbios_print_str("Part Number", table, table->part_number);
+	printf("\tAttributes: 0x%04x\n", table->attributes);
+	printf("\tExtended Size: 0x%08x\n", table->ext_size);
+	printf("\tConfigured Memory Speed: 0x%04x\n", table->config_mem_speed);
+	printf("\tMinimum voltage: 0x%04x\n", table->min_voltage);
+	printf("\tMaximum voltage: 0x%04x\n", table->max_voltage);
+	printf("\tConfigured voltage: 0x%04x\n", table->config_voltage);
+	smbios_print_lookup_str(md_tech_strings, table->mem_tech,
+				ARRAY_SIZE(md_tech_strings),
+				"Memory Technology");
+	printf("\tMemory Operating Mode Capability: 0x%04x\n",
+	       table->mem_op_mode_cap);
+	smbios_print_str("Firmware Version", table, table->fw_ver);
+	printf("\tModule Manufacturer ID: 0x%04x\n", table->module_man_id);
+	printf("\tModule Product ID: 0x%04x\n", table->module_prod_id);
+	printf("\tMemory Subsystem Controller Manufacturer ID: 0x%04x\n",
+	       table->mem_subsys_con_man_id);
+	printf("\tMemory Subsystem Controller Product ID: 0x%04x\n",
+	       table->mem_subsys_con_prod_id);
+	printf("\tNon-volatile Size: 0x%016llx\n", table->nonvolatile_size);
+	printf("\tVolatile Size: 0x%016llx\n", table->volatile_size);
+	printf("\tCache Size: 0x%016llx\n", table->cache_size);
+	printf("\tLogical Size: 0x%016llx\n", table->logical_size);
+	printf("\tExtended Speed: 0x%04x\n", table->ext_speed);
+	printf("\tExtended Configured Memory Speed: 0x%04x\n",
+	       table->ext_config_mem_speed);
+	printf("\tPMIC0 Manufacturer ID: 0x%04x\n", table->pmic0_man_id);
+	printf("\tPMIC0 Revision Number: 0x%04x\n", table->pmic0_rev_num);
+	printf("\tRCD Manufacturer ID: 0x%04x\n", table->rcd_man_id);
+	printf("\tRCD Revision Number: 0x%04x\n", table->rcd_rev_num);
+}
+
 static void smbios_print_type127(struct smbios_type127 *table)
 {
 	printf("End Of Table\n");
@@ -642,6 +765,9 @@ static int do_smbios(struct cmd_tbl *cmdtp, int flag, int argc,
 		case SMBIOS_PHYS_MEMORY_ARRAY:
 			smbios_print_type16((struct smbios_type16 *)pos);
 			break;
+		case SMBIOS_MEMORY_DEVICE:
+			smbios_print_type17((struct smbios_type17 *)pos);
+			break;
 		case SMBIOS_END_OF_TABLE:
 			smbios_print_type127((struct smbios_type127 *)pos);
 			break;
diff --git a/include/smbios.h b/include/smbios.h
index 16438c059c7..45131416253 100644
--- a/include/smbios.h
+++ b/include/smbios.h
@@ -327,6 +327,51 @@ struct __packed smbios_type16 {
 	u64 ext_max_cap;
 	char eos[SMBIOS_STRUCT_EOS_BYTES];
 };
+
+struct __packed smbios_type17 {
+	struct smbios_header hdr;
+	u16 phy_mem_array_hdl;
+	u16 mem_err_info_hdl;
+	u32 total_width;
+	u32 data_width;
+	u16 size;
+	u8 form_factor;
+	u8 dev_set;
+	u8 dev_locator;
+	u8 bank_locator;
+	u8 mem_type;
+	u16 type_detail;
+	u16 speed;
+	u8 manufacturer;
+	u8 serial_number;
+	u8 asset_tag;
+	u8 part_number;
+	u8 attributes;
+	u32 ext_size;
+	u16 config_mem_speed;
+	u16 min_voltage;
+	u16 max_voltage;
+	u16 config_voltage;
+	u8 mem_tech;
+	u16 mem_op_mode_cap;
+	u8 fw_ver;
+	u16 module_man_id;
+	u16 module_prod_id;
+	u16 mem_subsys_con_man_id;
+	u16 mem_subsys_con_prod_id;
+	u64 nonvolatile_size;
+	u64 volatile_size;
+	u64 cache_size;
+	u64 logical_size;
+	u32 ext_speed;
+	u32 ext_config_mem_speed;
+	u16 pmic0_man_id;
+	u16 pmic0_rev_num;
+	u16 rcd_man_id;
+	u16 rcd_rev_num;
+	char eos[SMBIOS_STRUCT_EOS_BYTES];
+};
+
 struct __packed smbios_type32 {
 	u8 type;
 	u8 length;
diff --git a/include/smbios_def.h b/include/smbios_def.h
index c6850a5d6f5..ce913f2f32a 100644
--- a/include/smbios_def.h
+++ b/include/smbios_def.h
@@ -309,4 +309,131 @@
 #define SMBIOS_MA_ERRINFO_NONE		0xFFFE
 #define SMBIOS_MA_ERRINFO_NOERR		0xFFFF
 
+/* Memory Device */
+
+/* Size */
+
+#define SMBIOS_MD_SIZE_UNKNOWN	0xFFFF
+#define SMBIOS_MD_SIZE_EXT	0x7FFF
+
+/* Form Factor */
+#define SMBIOS_MD_FF_OTHER	1
+#define SMBIOS_MD_FF_UNKNOWN	2
+#define SMBIOS_MD_FF_SIMM	3
+#define SMBIOS_MD_FF_SIP	4
+#define SMBIOS_MD_FF_CHIP	5
+#define SMBIOS_MD_FF_DIP	6
+#define SMBIOS_MD_FF_ZIP	7
+#define SMBIOS_MD_FF_PROPCARD	8
+#define SMBIOS_MD_FF_DIMM	9
+#define SMBIOS_MD_FF_TSOP	10
+#define SMBIOS_MD_FF_ROC	11
+#define SMBIOS_MD_FF_RIMM	12
+#define SMBIOS_MD_FF_SODIMM	13
+#define SMBIOS_MD_FF_SRIMM	14
+#define SMBIOS_MD_FF_FBDIMM	15
+#define SMBIOS_MD_FF_DIE	16
+
+/* Device set */
+#define SMBIOS_MD_DEVSET_NONE		0
+#define SMBIOS_MD_DEVSET_UNKNOWN	0xFF
+
+/* Speed */
+#define SMBIOS_MD_SPEED_UNKNOWN	0
+#define SMBIOS_MD_SPEED_EXT	0xFFFF
+
+/* Attributes */
+#define SMBIOS_MD_ATTR_RANK_UNKNOWN	0
+
+/* Configured Memory Speed */
+#define SMBIOS_MD_CONFSPEED_UNKNOWN	0
+#define SMBIOS_MD_CONFSPEED_EXT		0xFFFF
+
+/* Voltage */
+#define SMBIOS_MD_VOLTAGE_UNKNOWN	0
+
+/* Type */
+#define SMBIOS_MD_TYPE_OTHER	1
+#define SMBIOS_MD_TYPE_UNKNOWN	2
+#define SMBIOS_MD_TYPE_DRAM	3
+#define SMBIOS_MD_TYPE_EDRAM	4
+#define SMBIOS_MD_TYPE_VRAM	5
+#define SMBIOS_MD_TYPE_SRAM	6
+#define SMBIOS_MD_TYPE_RAM	7
+#define SMBIOS_MD_TYPE_ROM	8
+#define SMBIOS_MD_TYPE_FLASH	9
+#define SMBIOS_MD_TYPE_EEPROM	10
+#define SMBIOS_MD_TYPE_FEPROM	11
+#define SMBIOS_MD_TYPE_EPROM	12
+#define SMBIOS_MD_TYPE_CDRAM	13
+#define SMBIOS_MD_TYPE_3DRAM	14
+#define SMBIOS_MD_TYPE_SDRAM	15
+#define SMBIOS_MD_TYPE_SGRAM	16
+#define SMBIOS_MD_TYPE_RDRAM	17
+#define SMBIOS_MD_TYPE_DDR	18
+#define SMBIOS_MD_TYPE_DDR2	19
+#define SMBIOS_MD_TYPE_DDR2FBD	20
+#define SMBIOS_MD_TYPE_RSVD1	21
+#define SMBIOS_MD_TYPE_RSVD2	22
+#define SMBIOS_MD_TYPE_DSVD3	23
+#define SMBIOS_MD_TYPE_DDR3	24
+#define SMBIOS_MD_TYPE_FBD2	25
+#define SMBIOS_MD_TYPE_DDR4	26
+#define SMBIOS_MD_TYPE_LPDDR	27
+#define SMBIOS_MD_TYPE_LPDDR2	28
+#define SMBIOS_MD_TYPE_LPDDR3	29
+#define SMBIOS_MD_TYPE_LPDDR4	30
+#define SMBIOS_MD_TYPE_LNVD	31
+#define SMBIOS_MD_TYPE_HBM	32
+#define SMBIOS_MD_TYPE_HBM2	33
+#define SMBIOS_MD_TYPE_DDR5	34
+#define SMBIOS_MD_TYPE_LPDDR5	35
+#define SMBIOS_MD_TYPE_HBM3	36
+
+/* Type Detail */
+#define SMBIOS_MD_TD_RSVD	1 /* BIT(0), set to 0 */
+#define SMBIOS_MD_TD_OTHER	2 /* BIT(1) */
+#define SMBIOS_MD_TD_UNKNOWN	4 /* BIT(2) */
+#define SMBIOS_MD_TD_FP		8 /* BIT(3) */
+#define SMBIOS_MD_TD_SC		0x10 /* BIT(4) */
+#define SMBIOS_MD_TD_PS		0x20 /* BIT(5) */
+#define SMBIOS_MD_TD_RAMBUS	0x40 /* BIT(6) */
+#define SMBIOS_MD_TD_SYNC	0x80 /* BIT(7) */
+#define SMBIOS_MD_TD_CMOS	0x100 /* BIT(8) */
+#define SMBIOS_MD_TD_EDO	0x200 /* BIT(9) */
+#define SMBIOS_MD_TD_WINDRAM	0x400 /* BIT(10) */
+#define SMBIOS_MD_TD_CACHEDRAM	0x800 /* BIT(11) */
+#define SMBIOS_MD_TD_NV		0x1000 /* BIT(12) */
+#define SMBIOS_MD_TD_RGSTD	0x2000 /* BIT(13) */
+#define SMBIOS_MD_TD_UNRGSTD	0x4000 /* BIT(14) */
+#define SMBIOS_MD_TD_LRDIMM	0x8000 /* BIT(15) */
+
+/* Technology */
+#define SMBIOS_MD_TECH_OTHER	1
+#define SMBIOS_MD_TECH_UNKNOWN	2
+#define SMBIOS_MD_TECH_DRAM	3
+#define SMBIOS_MD_TECH_NVDIMMN	4
+#define SMBIOS_MD_TECH_NVDIMMF	5
+#define SMBIOS_MD_TECH_NVDIMMP	6
+#define SMBIOS_MD_TECH_OPTANE	7
+
+/* Operating Mode Capability */
+#define SMBIOS_MD_OPMC_RSVD	1 /* BIT(0), set to 0 */
+#define SMBIOS_MD_OPMC_OTHER	2 /* BIT(1) */
+#define SMBIOS_MD_OPMC_UNKNOWN	4 /* BIT(2) */
+#define SMBIOS_MD_OPMC_VM	8 /* BIT(3) */
+#define SMBIOS_MD_OPMC_BYTEAPM	0x10 /* BIT(4) */
+#define SMBIOS_MD_OPMC_BLKAPM	0x20 /* BIT(5) */
+/* Bit 6:15 Reserved, set to 0 */
+
+/* Non-volatile / Volatile / Cache / Logical portion Size */
+#define SMBIOS_MD_PORT_SIZE_NONE	0
+#define SMBIOS_MD_PORT_SIZE_UNKNOWN_HI	0xFFFFFFFF
+#define SMBIOS_MD_PORT_SIZE_UNKNOWN_LO	0xFFFFFFFF
+#define SMBIOS_MS_PORT_SIZE_UNKNOWN	0xFFFFFFFFFFFFFFFF
+
+/* Error Information Handle */
+#define SMBIOS_MD_ERRINFO_NONE		0xFFFE
+#define SMBIOS_MD_ERRINFO_NOERR		0xFFFF
+
 #endif /* _SMBIOS_DEF_H_ */
diff --git a/lib/smbios.c b/lib/smbios.c
index 27c9c975cf2..8a3fa42cef5 100644
--- a/lib/smbios.c
+++ b/lib/smbios.c
@@ -135,6 +135,14 @@ typedef int (*smbios_write_subnode)(ulong *current, int handle,
 				   struct smbios_ctx *ctx, int idx,
 				   int type);
 
+typedef int (*smbios_write_memnode)(ulong *current, int handle,
+				    struct smbios_ctx *ctx, int idx,
+				    int type);
+
+typedef int (*smbios_write_memctrlnode)(ulong *current, int handle,
+				      struct smbios_ctx *ctx, int idx,
+				      u64 base, u64 sz);
+
 /**
  * Function prototype to write a specific type of SMBIOS structure
  *
@@ -1432,6 +1440,374 @@ static int smbios_write_type16(ulong *current, int *handle,
 	return len;
 }
 
+static void smbios_pop_type17_general_si(struct smbios_ctx *ctx,
+					 struct smbios_type17 *t)
+{
+	t->mem_err_info_hdl =
+		smbios_get_val_si(ctx, "memory-error-information-handle",
+				  SYSID_NONE, SMBIOS_MD_ERRINFO_NONE);
+	t->total_width = smbios_get_val_si(ctx, "total-width", SYSID_NONE, 0);
+	t->data_width = smbios_get_val_si(ctx, "data-width", SYSID_NONE, 0);
+	t->form_factor = smbios_get_val_si(ctx, "form-factor",
+					   SYSID_NONE, SMBIOS_MD_FF_UNKNOWN);
+	t->dev_set = smbios_get_val_si(ctx, "device-set", SYSID_NONE,
+				       SMBIOS_MD_DEVSET_UNKNOWN);
+	t->data_width = smbios_get_val_si(ctx, "data-width", SYSID_NONE, 0);
+	t->dev_locator = smbios_add_prop_si(ctx, "device-locator", SYSID_NONE,
+					    NULL);
+	t->bank_locator = smbios_add_prop_si(ctx, "bank-locator", SYSID_NONE,
+					     NULL);
+	t->mem_type = smbios_get_val_si(ctx, "memory-type",
+					SYSID_NONE, SMBIOS_MD_TYPE_UNKNOWN);
+	t->type_detail = smbios_get_val_si(ctx, "type-detail",
+					   SYSID_NONE, SMBIOS_MD_TD_UNKNOWN);
+	t->speed = smbios_get_val_si(ctx, "speed", SYSID_NONE,
+				     SMBIOS_MD_SPEED_UNKNOWN);
+	t->manufacturer = smbios_add_prop_si(ctx, "manufacturer", SYSID_NONE,
+					     NULL);
+	t->serial_number = smbios_add_prop_si(ctx, "serial-number", SYSID_NONE,
+					      NULL);
+	t->asset_tag = smbios_add_prop_si(ctx, "asset-tag", SYSID_NONE, NULL);
+	t->part_number = smbios_add_prop_si(ctx, "part-number", SYSID_NONE,
+					    NULL);
+	t->attributes = smbios_get_val_si(ctx, "attributes", SYSID_NONE,
+					  SMBIOS_MD_ATTR_RANK_UNKNOWN);
+	t->config_mem_speed = smbios_get_val_si(ctx, "configured-memory-speed",
+						SYSID_NONE,
+						SMBIOS_MD_CONFSPEED_UNKNOWN);
+	t->min_voltage = smbios_get_val_si(ctx, "minimum-voltage", SYSID_NONE,
+					   SMBIOS_MD_VOLTAGE_UNKNOWN);
+	t->max_voltage = smbios_get_val_si(ctx, "maximum-voltage", SYSID_NONE,
+					   SMBIOS_MD_VOLTAGE_UNKNOWN);
+	t->config_voltage = smbios_get_val_si(ctx, "configured-voltage",
+					      SYSID_NONE,
+					      SMBIOS_MD_VOLTAGE_UNKNOWN);
+	t->mem_tech = smbios_get_val_si(ctx, "memory-technology",
+					SYSID_NONE, SMBIOS_MD_TECH_UNKNOWN);
+	t->mem_op_mode_cap =
+		smbios_get_val_si(ctx, "memory-operating-mode-capability",
+				  SYSID_NONE, SMBIOS_MD_OPMC_UNKNOWN);
+	t->fw_ver = smbios_add_prop_si(ctx, "firmware-version", SYSID_NONE,
+				       NULL);
+	t->module_man_id = smbios_get_val_si(ctx, "module-manufacturer-id",
+					     SYSID_NONE, 0);
+	t->module_prod_id = smbios_get_val_si(ctx, "module-product-id",
+					      SYSID_NONE, 0);
+	t->mem_subsys_con_man_id =
+		smbios_get_val_si(ctx,
+				  "memory-subsystem-controller-manufacturer-id",
+				  SYSID_NONE, 0);
+	t->mem_subsys_con_prod_id =
+		smbios_get_val_si(ctx,
+				  "memory-subsystem-controller-product-id",
+				  SYSID_NONE, 0);
+	t->nonvolatile_size = smbios_get_u64_si(ctx, "non-volatile-size",
+						SYSID_NONE,
+						SMBIOS_MS_PORT_SIZE_UNKNOWN);
+	t->volatile_size = smbios_get_u64_si(ctx, "volatile-size",
+					     SYSID_NONE,
+					     SMBIOS_MS_PORT_SIZE_UNKNOWN);
+	t->cache_size = smbios_get_u64_si(ctx, "cache-size",
+					  SYSID_NONE,
+					  SMBIOS_MS_PORT_SIZE_UNKNOWN);
+	t->logical_size = smbios_get_u64_si(ctx, "logical-size",
+					    SYSID_NONE,
+					    SMBIOS_MS_PORT_SIZE_UNKNOWN);
+	t->ext_speed = smbios_get_val_si(ctx, "extended-speed", SYSID_NONE, 0);
+	t->ext_config_mem_speed =
+		smbios_get_val_si(ctx, "extended-configured-memory-speed",
+				  SYSID_NONE, 0);
+	t->pmic0_man_id = smbios_get_val_si(ctx, "pmic0-manufacturer-id",
+					    SYSID_NONE, 0);
+	t->pmic0_rev_num = smbios_get_val_si(ctx, "pmic0-revision-number",
+					     SYSID_NONE, 0);
+	t->rcd_man_id = smbios_get_val_si(ctx, "rcd-manufacturer-id",
+					  SYSID_NONE, 0);
+	t->rcd_rev_num = smbios_get_val_si(ctx, "rcd-revision-number",
+					   SYSID_NONE, 0);
+}
+
+static void
+smbios_pop_type17_size_from_memory_node(ofnode node, struct smbios_type17 *t)
+{
+	const fdt32_t *reg;
+	int len;
+	u64 sz;
+	u32 size_mb;
+
+	/* Read property 'reg' from the node */
+	reg = ofnode_read_prop(node, "reg", &len);
+	if (!reg || len < sizeof(fdt32_t) * 4 || len % sizeof(fdt32_t))
+		return;
+
+	/* Combine hi/lo for size (typically 64-bit) */
+	sz = ((u64)fdt32_to_cpu(reg[2]) << 32) | fdt32_to_cpu(reg[3]);
+
+	/* Convert size to MB */
+	size_mb = (u32)(sz >> 20); /* 1 MB = 2^20 */
+	if (size_mb < SMBIOS_MD_SIZE_EXT) {
+		t->size = cpu_to_le16(size_mb);
+		t->ext_size = 0;
+		return;
+	}
+
+	t->size = cpu_to_le16(SMBIOS_MD_SIZE_EXT); /* Signal extended used */
+	t->ext_size = cpu_to_le32((u32)(sz >> 10)); /* In KB */
+}
+
+static void smbios_pop_type17_size_si(struct smbios_ctx *ctx,
+				      struct smbios_type17 *t)
+{
+	t->size = smbios_get_val_si(ctx, "size", SYSID_NONE,
+				    SMBIOS_MD_SIZE_UNKNOWN);
+	t->ext_size = smbios_get_val_si(ctx, "extended-size", SYSID_NONE, 0);
+}
+
+static int
+smbios_scan_memctrl_subnode(ulong *current, int *handle, struct smbios_ctx *ctx,
+			    int idx, smbios_write_memctrlnode cb)
+{
+	int total_len = 0;
+	ofnode child;
+	int i = 0;
+	int hdl_base = *handle;
+	u64 base = 0;
+
+	/*
+	 * Enumerate all subnodes of 'memory-controller' that contain 'size'
+	 * property and generate one instance for each.
+	 */
+	for (child = ofnode_first_subnode(ctx->node); ofnode_valid(child);
+	     child = ofnode_next_subnode(child)) {
+		u64 sz = 0;
+		const fdt32_t *size;
+		int proplen;
+
+		size = ofnode_read_prop(child, "size", &proplen);
+		if (!size || proplen < sizeof(fdt32_t) ||
+		    proplen % sizeof(fdt32_t))
+			continue;
+
+		/* 64-bit size: <hi lo> or 32-bit size */
+		if (proplen >= sizeof(fdt32_t) * 2)
+			sz = ((u64)fdt32_to_cpu(size[0]) << 32) |
+			     fdt32_to_cpu(size[1]);
+		else
+			sz = fdt32_to_cpu(size[0]);
+
+		*handle = hdl_base + i;
+		total_len += cb(current, *handle, ctx, idx, base, sz);
+		base += sz;
+		i++;
+	}
+
+	return total_len;
+}
+
+static int
+smbios_write_type17_from_memctrl_node(ulong *current, int handle,
+				      struct smbios_ctx *ctx, int idx,
+				      u64 __maybe_unused base, u64 sz)
+{
+	struct smbios_type17 *t;
+	int len;
+	u8 *eos_addr;
+	u32 size_mb;
+	void *hdl;
+	size_t hdl_size;
+
+	len = sizeof(*t);
+	t = map_sysmem(*current, len);
+	memset(t, 0, len);
+
+	fill_smbios_header(t, SMBIOS_MEMORY_DEVICE, len, handle);
+
+	/* eos is at the end of the structure */
+	eos_addr = (u8 *)t + len - sizeof(t->eos);
+	smbios_set_eos(ctx, eos_addr);
+
+	/* Read the memory array handles */
+	if (!sysinfo_get_data(ctx->dev, SYSID_SM_MEMARRAY_HANDLE, &hdl,
+			      &hdl_size) &&
+		hdl_size == SYSINFO_MEM_HANDLE_MAX * sizeof(u16))
+		t->phy_mem_array_hdl = *((u16 *)hdl + idx);
+
+	/* Convert to MB */
+	size_mb = (u32)(sz >> 20);
+	if (size_mb < SMBIOS_MD_SIZE_EXT) {
+		/* Use 16-bit size field */
+		t->size = cpu_to_le16(size_mb);  /* In MB */
+		t->ext_size = cpu_to_le32(0);
+	} else {
+		/* Signal use of extended size field */
+		t->size = cpu_to_le16(SMBIOS_MD_SIZE_EXT);
+		t->ext_size = cpu_to_le32((u32)(sz >> 10)); /* In KB */
+	}
+
+	/* Write other general fields */
+	smbios_pop_type17_general_si(ctx, t);
+
+	len = t->hdr.length + smbios_string_table_len(ctx);
+	*current += len;
+	unmap_sysmem(t);
+
+	return len;
+}
+
+static int smbios_write_type17_mem(ulong *current, int handle,
+				   struct smbios_ctx *ctx, int idx,
+				   int type)
+{
+	struct smbios_type17 *t;
+	int len;
+	u8 *eos_addr;
+	void *hdl;
+	size_t hdl_size;
+
+	len = sizeof(*t);
+	t = map_sysmem(*current, len);
+	memset(t, 0, len);
+
+	fill_smbios_header(t, SMBIOS_MEMORY_DEVICE, len, handle);
+
+	/* eos is at the end of the structure */
+	eos_addr = (u8 *)t + len - sizeof(t->eos);
+	smbios_set_eos(ctx, eos_addr);
+
+	if (type == SMBIOS_MEM_CUSTOM) {
+		smbios_pop_type17_size_si(ctx, t);
+
+		t->phy_mem_array_hdl =
+			smbios_get_val_si(ctx, "physical-memory-array-handle",
+					  SYSID_NONE, 0);
+	} else if (type == SMBIOS_MEM_FDT_MEM_NODE) {
+		smbios_pop_type17_size_from_memory_node(ctx->node, t);
+
+		/* Read the memory array handles */
+		if (!sysinfo_get_data(ctx->dev, SYSID_SM_MEMARRAY_HANDLE, &hdl,
+				      &hdl_size) &&
+		    hdl_size == SYSINFO_MEM_HANDLE_MAX * sizeof(u16))
+			t->phy_mem_array_hdl = *((u16 *)hdl + idx);
+	}
+
+	/* Write other general fields */
+	smbios_pop_type17_general_si(ctx, t);
+
+	len = t->hdr.length + smbios_string_table_len(ctx);
+	*current += len;
+	unmap_sysmem(t);
+
+	return len;
+}
+
+static int smbios_scan_mem_nodes(ulong *current, int *handle,
+				 struct smbios_ctx *ctx,
+				 smbios_write_memnode mem_cb,
+				 int *idx)
+{
+	int len = 0;
+	struct smbios_ctx ctx_bak;
+	ofnode child;
+	int hdl_base = *handle;
+
+	memcpy(&ctx_bak, ctx, sizeof(ctx_bak));
+
+	for (child = ofnode_first_subnode(ofnode_root());
+	     ofnode_valid(child); child = ofnode_next_subnode(child)) {
+		const char *str;
+
+		/* Look up for 'device_type = "memory"' */
+		str = ofnode_read_string(child, "device_type");
+		if (!str || strcmp(str, "memory"))
+			continue;
+
+		ctx->node = child;
+		*handle = hdl_base + *idx;
+		/* Generate one instance for each 'memory' node */
+		len += mem_cb(current, *handle, ctx, *idx,
+			      SMBIOS_MEM_FDT_MEM_NODE);
+		memcpy(ctx, &ctx_bak, sizeof(*ctx));
+		(*idx)++;
+	}
+
+	return len;
+}
+
+static int smbios_scan_mctrl_subnodes(ulong *current, int *handle,
+				      struct smbios_ctx *ctx,
+				      smbios_write_memctrlnode mctrl_wcb,
+				      int *idx)
+{
+	int len = 0;
+	struct smbios_ctx ctx_bak;
+	ofnode child;
+
+	memcpy(&ctx_bak, ctx, sizeof(ctx_bak));
+
+	for (child = ofnode_first_subnode(ofnode_root());
+	     ofnode_valid(child); child = ofnode_next_subnode(child)) {
+		const char *compat;
+		const char *name;
+
+		/*
+		 * Look up for node with name or property 'compatible'
+		 * containing 'memory-controller'.
+		 */
+		name = ofnode_get_name(child);
+		compat = ofnode_read_string(child, "compatible");
+		if ((!compat || !strstr(compat, "memory-controller")) &&
+		    (!name || !strstr(name, "memory-controller")))
+			continue;
+
+		(*handle)++;
+		ctx->node = child;
+		/*
+		 * Generate one instance for each subnode of
+		 * 'memory-controller' which contains property 'size'.
+		 */
+		len += smbios_scan_memctrl_subnode(current, handle, ctx,
+						   *idx, mctrl_wcb);
+		memcpy(ctx, &ctx_bak, sizeof(*ctx));
+		(*idx)++;
+	}
+	return len;
+}
+
+static int smbios_write_type1719(ulong *current, int *handle,
+				 struct smbios_ctx *ctx,
+				 smbios_write_memnode mem_cb,
+				 smbios_write_memctrlnode mctrl_wcb)
+{
+	int len = 0;
+	int idx;
+
+	if (!IS_ENABLED(CONFIG_OF_CONTROL))
+		return 0;	/* Error, return 0-length */
+
+	/* Step 1: Scan any subnode exists */
+	len = smbios_scan_subnodes(current, ctx, handle, mem_cb,
+				   SMBIOS_MEM_CUSTOM);
+	if (len)
+		return len;
+
+	/* Step 2: Scan 'memory' node from the entire FDT */
+	idx = 0;
+	len += smbios_scan_mem_nodes(current, handle, ctx, mem_cb, &idx);
+
+	/* Step 3: Scan 'memory-controller' node from the entire FDT */
+	len += smbios_scan_mctrl_subnodes(current, handle, ctx, mctrl_wcb, &idx);
+
+	return len;
+}
+
+static int smbios_write_type17(ulong *current, int *handle,
+			       struct smbios_ctx *ctx)
+{
+	return smbios_write_type1719(current, handle, ctx,
+				     smbios_write_type17_mem,
+				     smbios_write_type17_from_memctrl_node);
+}
+
 #endif /* #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) */
 
 static int smbios_write_type32(ulong *current, int *handle,
@@ -1481,6 +1857,7 @@ static struct smbios_write_method smbios_write_funcs[] = {
 #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE)
 	{ smbios_write_type9, "system-slot"},
 	{ smbios_write_type16, "memory-array"},
+	{ smbios_write_type17, "memory-device"},
 #endif
 	{ smbios_write_type32, },
 	{ smbios_write_type127 },
-- 
2.25.1



More information about the U-Boot mailing list