diff options
| author | Raymond Mao <[email protected]> | 2026-02-13 17:52:48 -0500 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-02-18 08:27:51 -0600 |
| commit | 374896158b435843d8ecf2c04c9985b0321295e0 (patch) | |
| tree | 7dadbe3d74e4fbcc11f8ff4da380d3df19fbf1a3 /lib | |
| parent | 83b28b55d74f884a30c47238dc8bdf9fbe6e495c (diff) | |
smbios: add support for dynamic generation of Type 16 table
This commit implements SMBIOS Type 16 (Physical Memory Array)
generation with a hybrid approach supporting both:
1. Explicit definition via Device Tree 'smbios' node:
Child node under '/smbios/smbios/memory-array' will be used to
populate as individual Type 16 structure directly.
- Properties follow SMBIOS field names with lowercase letters and
hyphen-separated words (e.g., 'memory-error-correction',
'maximum-capacity', 'extended-maximum-capacity', 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-array' does not exist,
the implementation will:
- Scan all top-level 'memory@' nodes to populate Type 16 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 16 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 <[email protected]>
Tested-by: Ilias Apalodimas <[email protected]>
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/smbios.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/lib/smbios.c b/lib/smbios.c index caeb309294d..27c9c975cf2 100644 --- a/lib/smbios.c +++ b/lib/smbios.c @@ -290,6 +290,49 @@ static int smbios_get_val_si(struct smbios_ctx * __maybe_unused ctx, return val_def; } +#if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) +static u64 smbios_get_u64_si(struct smbios_ctx * __maybe_unused ctx, + const char * __maybe_unused prop, + int __maybe_unused sysinfo_id, u64 val_def) +{ + size_t len; + void *data; + const fdt32_t *prop_val; + int prop_len; + u64 val = 0; + + if (!ctx->dev) + return val_def; + + if (!sysinfo_get_data(ctx->dev, sysinfo_id, &data, &len)) + return *((u64 *)data); + + if (!IS_ENABLED(CONFIG_OF_CONTROL) || !prop || !ofnode_valid(ctx->node)) + return val_def; + + prop_val = ofnode_read_prop(ctx->node, prop, &prop_len); + if (!prop_val || prop_len < sizeof(fdt32_t) || + prop_len % sizeof(fdt32_t)) { + /* + * If the node or property is not valid fallback and try the root + */ + prop_val = ofnode_read_prop(ofnode_root(), prop, &prop_len); + if (!prop_val || prop_len < sizeof(fdt32_t) || + prop_len % sizeof(fdt32_t)) + return val_def; + } + + /* 64-bit: <hi lo> or 32-bit */ + if (prop_len >= sizeof(fdt32_t) * 2) { + val = ((u64)fdt32_to_cpu(prop_val[0]) << 32) | + fdt32_to_cpu(prop_val[1]); + } else { + val = fdt32_to_cpu(prop_val[0]); + } + return val; +} +#endif + /** * smbios_add_prop_si() - Add a property from the devicetree or sysinfo * @@ -1151,6 +1194,244 @@ static int smbios_write_type9(ulong *current, int *handle, return len; } +static u64 smbios_pop_size_from_memory_node(ofnode node) +{ + const fdt32_t *reg; + int len; + u64 size_bytes; + + /* 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 0; + + /* Combine hi/lo for size (typically 64-bit) */ + size_bytes = ((u64)fdt32_to_cpu(reg[2]) << 32) | fdt32_to_cpu(reg[3]); + + return size_bytes; +} + +static int +smbios_write_type16_sum_memory_nodes(ulong *current, int handle, + struct smbios_ctx *ctx, u16 cnt, u64 size) +{ + struct smbios_type16 *t; + int len = sizeof(*t); + u8 *eos_addr; + void *hdl; + size_t hdl_size; + + t = map_sysmem(*current, len); + memset(t, 0, len); + + fill_smbios_header(t, SMBIOS_PHYS_MEMORY_ARRAY, len, handle); + + /* eos is at the end of the structure */ + eos_addr = (u8 *)t + len - sizeof(t->eos); + smbios_set_eos(ctx, eos_addr); + + /* default attributes */ + t->location = SMBIOS_MA_LOCATION_MOTHERBOARD; + t->use = SMBIOS_MA_USE_SYSTEM; + t->mem_err_corr = SMBIOS_MA_ERRCORR_UNKNOWN; + t->mem_err_info_hdl = SMBIOS_MA_ERRINFO_NONE; + t->num_of_mem_dev = cnt; + + /* Use extended field */ + t->max_cap = cpu_to_le32(0x80000000); + t->ext_max_cap = cpu_to_le64(size >> 10); /* In KB */ + + /* Save 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)) + *((u16 *)hdl) = handle; + + len = t->hdr.length + smbios_string_table_len(ctx); + *current += len; + unmap_sysmem(t); + + return len; +} + +static void +smbios_pop_type16_from_memcontroller_node(ofnode node, struct smbios_type16 *t) +{ + ofnode child; + int count = 0; + u64 total = 0; + + /* default attributes */ + t->location = SMBIOS_MA_LOCATION_MOTHERBOARD; + t->use = SMBIOS_MA_USE_SYSTEM; + t->mem_err_info_hdl = SMBIOS_MA_ERRINFO_NONE; + + /* Check custom property 'ecc-enabled' */ + if (ofnode_read_bool(node, "ecc-enabled")) + t->mem_err_corr = SMBIOS_MA_ERRCORR_SBITECC; + else + t->mem_err_corr = SMBIOS_MA_ERRCORR_UNKNOWN; + + /* Read subnodes with 'size' property */ + for (child = ofnode_first_subnode(node); ofnode_valid(child); + child = ofnode_next_subnode(child)) { + u64 sz = 0; + const fdt32_t *size; + int len; + + size = ofnode_read_prop(child, "size", &len); + if (!size || len < sizeof(fdt32_t) || len % sizeof(fdt32_t)) + continue; + + /* 64-bit size: <hi lo> or 32-bit size */ + if (len >= sizeof(fdt32_t) * 2) + sz = ((u64)fdt32_to_cpu(size[0]) << 32) | + fdt32_to_cpu(size[1]); + else + sz = fdt32_to_cpu(size[0]); + + count++; + total += sz; + } + + /* + * Number of memory devices associated with this array + * (i.e., how many Type17 entries link to this Type16 array) + */ + t->num_of_mem_dev = count; + + /* Use extended field */ + t->max_cap = cpu_to_le32(0x80000000); + t->ext_max_cap = cpu_to_le64(total >> 10); /* In KB */ +} + +static void smbios_pop_type16_si(struct smbios_ctx *ctx, + struct smbios_type16 *t) +{ + t->location = smbios_get_val_si(ctx, "location", SYSID_NONE, + SMBIOS_MA_LOCATION_UNKNOWN); + t->use = smbios_get_val_si(ctx, "use", SYSID_NONE, + SMBIOS_MA_USE_UNKNOWN); + t->mem_err_corr = smbios_get_val_si(ctx, "memory-error-correction", SYSID_NONE, + SMBIOS_MA_ERRCORR_UNKNOWN); + t->max_cap = smbios_get_val_si(ctx, "maximum-capacity", SYSID_NONE, 0); + t->mem_err_info_hdl = smbios_get_val_si(ctx, "memory-error-information-handle", + SYSID_NONE, SMBIOS_MA_ERRINFO_NONE); + t->num_of_mem_dev = smbios_get_val_si(ctx, "number-of-memory-devices", SYSID_NONE, 1); + t->ext_max_cap = smbios_get_u64_si(ctx, "extended-maximum-capacity", SYSID_NONE, 0); +} + +static int smbios_write_type16_1array(ulong *current, int handle, + struct smbios_ctx *ctx, int idx, + int type) +{ + struct smbios_type16 *t; + int len = sizeof(*t); + u8 *eos_addr; + void *hdl; + size_t hdl_size; + + t = map_sysmem(*current, len); + memset(t, 0, len); + + fill_smbios_header(t, SMBIOS_PHYS_MEMORY_ARRAY, 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_type16_si(ctx, t); + else if (type == SMBIOS_MEM_FDT_MEMCON_NODE) + smbios_pop_type16_from_memcontroller_node(ctx->node, t); + + /* Save 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)) + *((u16 *)hdl + idx) = handle; + + len = t->hdr.length + smbios_string_table_len(ctx); + *current += len; + unmap_sysmem(t); + + return len; +} + +static int smbios_write_type16(ulong *current, int *handle, + struct smbios_ctx *ctx) +{ + int len; + struct smbios_ctx ctx_bak; + ofnode child; + int idx; + u64 total = 0; + int count = 0; + int hdl_base = *handle; + + if (!IS_ENABLED(CONFIG_OF_CONTROL)) + return 0; /* Error, return 0-length */ + + /* Step 1: Scan any subnode exists under 'memory-array' */ + len = smbios_scan_subnodes(current, ctx, handle, + smbios_write_type16_1array, + SMBIOS_MEM_CUSTOM); + if (len) + return len; + + /* Step 2: Scan 'memory' node from the entire FDT */ + 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")) { + count++; + total += smbios_pop_size_from_memory_node(child); + } + } + /* + * Generate one type16 instance for all 'memory' nodes, + * use idx=0 implicitly + */ + if (count) + len += smbios_write_type16_sum_memory_nodes(current, *handle, + ctx, count, total); + + /* Step 3: Scan 'memory-controller' node from the entire FDT */ + /* idx starts from 1 */ + memcpy(&ctx_bak, ctx, sizeof(ctx_bak)); + for (idx = 1, 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 = hdl_base + idx; + ctx->node = child; + /* + * Generate one type16 instance for each 'memory-controller' + * node, sum the 'size' of all subnodes. + */ + len += smbios_write_type16_1array(current, *handle, ctx, idx, + SMBIOS_MEM_FDT_MEMCON_NODE); + idx++; + memcpy(ctx, &ctx_bak, sizeof(*ctx)); + } + + return len; +} + #endif /* #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) */ static int smbios_write_type32(ulong *current, int *handle, @@ -1199,6 +1480,7 @@ static struct smbios_write_method smbios_write_funcs[] = { { smbios_write_type4, "processor"}, #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) { smbios_write_type9, "system-slot"}, + { smbios_write_type16, "memory-array"}, #endif { smbios_write_type32, }, { smbios_write_type127 }, |
