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

Heinrich Schuchardt xypron.glpk at gmx.de
Sun Nov 16 11:45:55 CET 2025


On 11/14/25 18:06, Raymond Mao wrote:
> 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);


When using QFW QEMU provides the SMBIOS table. It currently uses SMBIOS 
version 3.0. The fields below where introduced with version 3.2. So you 
are causing a buffer overrun here.

[PATCH 1/1] cmd: smbios: correct output for table type 4
https://lists.denx.de/pipermail/u-boot/2025-November/603413.html
starts introducing version checks to avoid buffer overruns.

Best regards

Heinrich


> +	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 },



More information about the U-Boot mailing list