[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