]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
721e992a BM |
2 | /* |
3 | * Copyright (C) 2015, Bin Meng <[email protected]> | |
4 | * | |
5 | * Adapted from coreboot src/arch/x86/smbios.c | |
721e992a BM |
6 | */ |
7 | ||
4a8a54c3 SG |
8 | #define LOG_CATEGORY LOGC_BOARD |
9 | ||
3b3c7280 | 10 | #include <display_options.h> |
78227d4e | 11 | #include <dm.h> |
7b51b576 | 12 | #include <env.h> |
11275e4f | 13 | #include <linux/stringify.h> |
738b3466 | 14 | #include <linux/string.h> |
a2505fc8 | 15 | #include <mapmem.h> |
4b6dddc2 | 16 | #include <smbios.h> |
07c9e683 | 17 | #include <sysinfo.h> |
4b6dddc2 | 18 | #include <tables_csum.h> |
721e992a | 19 | #include <version.h> |
738b3466 IA |
20 | #include <malloc.h> |
21 | #include <dm/ofnode.h> | |
96476206 AG |
22 | #ifdef CONFIG_CPU |
23 | #include <cpu.h> | |
96476206 AG |
24 | #include <dm/uclass-internal.h> |
25 | #endif | |
8c919fcd | 26 | #include <linux/sizes.h> |
721e992a | 27 | |
11275e4f T |
28 | /* Safeguard for checking that U_BOOT_VERSION_NUM macros are compatible with U_BOOT_DMI */ |
29 | #if U_BOOT_VERSION_NUM < 2000 || U_BOOT_VERSION_NUM > 2099 || \ | |
30 | U_BOOT_VERSION_NUM_PATCH < 1 || U_BOOT_VERSION_NUM_PATCH > 12 | |
31 | #error U_BOOT_VERSION_NUM macros are not compatible with DMI, fix U_BOOT_DMI macros | |
32 | #endif | |
33 | ||
34 | /* | |
35 | * U_BOOT_DMI_DATE contains BIOS Release Date in format mm/dd/yyyy. | |
36 | * BIOS Release Date is calculated from U-Boot version and fixed day 01. | |
37 | * So for U-Boot version 2021.04 it is calculated as "04/01/2021". | |
38 | * BIOS Release Date should contain date when code was released | |
39 | * and not when it was built or compiled. | |
40 | */ | |
41 | #if U_BOOT_VERSION_NUM_PATCH < 10 | |
42 | #define U_BOOT_DMI_MONTH "0" __stringify(U_BOOT_VERSION_NUM_PATCH) | |
43 | #else | |
44 | #define U_BOOT_DMI_MONTH __stringify(U_BOOT_VERSION_NUM_PATCH) | |
45 | #endif | |
46 | #define U_BOOT_DMI_DAY "01" | |
47 | #define U_BOOT_DMI_YEAR __stringify(U_BOOT_VERSION_NUM) | |
48 | #define U_BOOT_DMI_DATE U_BOOT_DMI_MONTH "/" U_BOOT_DMI_DAY "/" U_BOOT_DMI_YEAR | |
49 | ||
e9adaa75 SG |
50 | DECLARE_GLOBAL_DATA_PTR; |
51 | ||
738b3466 IA |
52 | /** |
53 | * struct map_sysinfo - Mapping of sysinfo strings to DT | |
54 | * | |
5e2b472b | 55 | * @si_str: sysinfo string |
738b3466 IA |
56 | * @dt_str: DT string |
57 | * @max: Max index of the tokenized string to pick. Counting starts from 0 | |
58 | * | |
59 | */ | |
60 | struct map_sysinfo { | |
b6488caa | 61 | const char *si_node; |
5e2b472b | 62 | const char *si_str; |
738b3466 IA |
63 | const char *dt_str; |
64 | int max; | |
65 | }; | |
66 | ||
67 | static const struct map_sysinfo sysinfo_to_dt[] = { | |
b6488caa IA |
68 | { .si_node = "system", .si_str = "product", .dt_str = "model", 2 }, |
69 | { .si_node = "system", .si_str = "manufacturer", .dt_str = "compatible", 1 }, | |
70 | { .si_node = "baseboard", .si_str = "product", .dt_str = "model", 2 }, | |
71 | { .si_node = "baseboard", .si_str = "manufacturer", .dt_str = "compatible", 1 }, | |
738b3466 IA |
72 | }; |
73 | ||
1e8989ad SG |
74 | /** |
75 | * struct smbios_ctx - context for writing SMBIOS tables | |
76 | * | |
b6488caa IA |
77 | * @node: node containing the information to write (ofnode_null() |
78 | * if none) | |
79 | * @dev: sysinfo device to use (NULL if none) | |
80 | * @subnode_name: sysinfo subnode_name. Used for DT fallback | |
81 | * @eos: end-of-string pointer for the table being processed. | |
82 | * This is set up when we start processing a table | |
83 | * @next_ptr: pointer to the start of the next string to be added. | |
84 | * When the table is not empty, this points to the byte | |
85 | * after the \0 of the previous string. | |
86 | * @last_str: points to the last string that was written to the table, | |
87 | * or NULL if none | |
1e8989ad SG |
88 | */ |
89 | struct smbios_ctx { | |
90 | ofnode node; | |
91 | struct udevice *dev; | |
b6488caa | 92 | const char *subnode_name; |
0c95fff3 | 93 | char *eos; |
fd3b826d | 94 | char *next_ptr; |
e9adaa75 | 95 | char *last_str; |
1e8989ad SG |
96 | }; |
97 | ||
0e89b859 SG |
98 | /** |
99 | * Function prototype to write a specific type of SMBIOS structure | |
100 | * | |
101 | * @addr: start address to write the structure | |
102 | * @handle: the structure's handle, a unique 16-bit number | |
1e8989ad | 103 | * @ctx: context for writing the tables |
8c6532d7 | 104 | * Return: size of the structure |
0e89b859 | 105 | */ |
1e8989ad SG |
106 | typedef int (*smbios_write_type)(ulong *addr, int handle, |
107 | struct smbios_ctx *ctx); | |
0e89b859 | 108 | |
44ffb6f0 SG |
109 | /** |
110 | * struct smbios_write_method - Information about a table-writing function | |
111 | * | |
112 | * @write: Function to call | |
113 | * @subnode_name: Name of subnode which has the information for this function, | |
114 | * NULL if none | |
115 | */ | |
116 | struct smbios_write_method { | |
117 | smbios_write_type write; | |
118 | const char *subnode_name; | |
119 | }; | |
120 | ||
b6488caa | 121 | static const struct map_sysinfo *convert_sysinfo_to_dt(const char *node, const char *si) |
738b3466 IA |
122 | { |
123 | int i; | |
124 | ||
125 | for (i = 0; i < ARRAY_SIZE(sysinfo_to_dt); i++) { | |
b6488caa IA |
126 | if (node && !strcmp(node, sysinfo_to_dt[i].si_node) && |
127 | !strcmp(si, sysinfo_to_dt[i].si_str)) | |
738b3466 IA |
128 | return &sysinfo_to_dt[i]; |
129 | } | |
130 | ||
131 | return NULL; | |
132 | } | |
133 | ||
721e992a BM |
134 | /** |
135 | * smbios_add_string() - add a string to the string area | |
136 | * | |
137 | * This adds a string to the string area which is appended directly after | |
138 | * the formatted portion of an SMBIOS structure. | |
139 | * | |
0c95fff3 | 140 | * @ctx: SMBIOS context |
721e992a | 141 | * @str: string to add |
6ebf9136 | 142 | * Return: string number in the string area. 0 if str is NULL. |
721e992a | 143 | */ |
0c95fff3 | 144 | static int smbios_add_string(struct smbios_ctx *ctx, const char *str) |
721e992a BM |
145 | { |
146 | int i = 1; | |
0c95fff3 SG |
147 | char *p = ctx->eos; |
148 | ||
6ebf9136 HS |
149 | if (!str) |
150 | return 0; | |
151 | ||
721e992a BM |
152 | for (;;) { |
153 | if (!*p) { | |
e9adaa75 | 154 | ctx->last_str = p; |
721e992a BM |
155 | strcpy(p, str); |
156 | p += strlen(str); | |
157 | *p++ = '\0'; | |
fd3b826d | 158 | ctx->next_ptr = p; |
721e992a BM |
159 | *p++ = '\0'; |
160 | ||
161 | return i; | |
162 | } | |
163 | ||
e9adaa75 SG |
164 | if (!strcmp(p, str)) { |
165 | ctx->last_str = p; | |
721e992a | 166 | return i; |
e9adaa75 | 167 | } |
721e992a BM |
168 | |
169 | p += strlen(p) + 1; | |
170 | i++; | |
171 | } | |
172 | } | |
173 | ||
738b3466 IA |
174 | /** |
175 | * get_str_from_dt - Get a substring from a DT property. | |
176 | * After finding the property in the DT, the function | |
177 | * will parse comma-separated values and return the value. | |
178 | * If nprop->max exceeds the number of comma-separated | |
179 | * elements, the last non NULL value will be returned. | |
180 | * Counting starts from zero. | |
181 | * | |
182 | * @nprop: sysinfo property to use | |
183 | * @str: pointer to fill with data | |
184 | * @size: str buffer length | |
185 | */ | |
186 | static | |
187 | void get_str_from_dt(const struct map_sysinfo *nprop, char *str, size_t size) | |
188 | { | |
189 | const char *dt_str; | |
190 | int cnt = 0; | |
191 | char *token; | |
192 | ||
193 | memset(str, 0, size); | |
194 | if (!nprop || !nprop->max) | |
195 | return; | |
196 | ||
197 | dt_str = ofnode_read_string(ofnode_root(), nprop->dt_str); | |
198 | if (!dt_str) | |
199 | return; | |
200 | ||
201 | memcpy(str, dt_str, size); | |
202 | token = strtok(str, ","); | |
203 | while (token && cnt < nprop->max) { | |
204 | strlcpy(str, token, strlen(token) + 1); | |
205 | token = strtok(NULL, ","); | |
206 | cnt++; | |
207 | } | |
208 | } | |
209 | ||
5d38e0d9 RM |
210 | /** |
211 | * smbios_get_val_si() - Get value from the devicetree or sysinfo | |
212 | * | |
213 | * @ctx: context of SMBIOS | |
214 | * @prop: property to read | |
215 | * @sysinfo_id: unique identifier for the value to be read | |
216 | * @val_def: Default value | |
217 | * Return: Valid value from sysinfo or device tree, otherwise val_def. | |
218 | */ | |
219 | static int smbios_get_val_si(struct smbios_ctx * __maybe_unused ctx, | |
220 | const char * __maybe_unused prop, | |
221 | int __maybe_unused sysinfo_id, int val_def) | |
222 | { | |
223 | #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) | |
224 | int val; | |
225 | ||
226 | if (!ctx->dev) | |
227 | return val_def; | |
228 | ||
229 | if (!sysinfo_get_int(ctx->dev, sysinfo_id, &val)) | |
230 | return val; | |
231 | ||
232 | if (!IS_ENABLED(CONFIG_OF_CONTROL) || !prop) | |
233 | return val_def; | |
234 | ||
235 | if (ofnode_valid(ctx->node) && !ofnode_read_u32(ctx->node, prop, &val)) | |
236 | return val; | |
237 | ||
238 | /* | |
239 | * If the node or property is not valid fallback and try the root | |
240 | */ | |
241 | if (!ofnode_read_u32(ofnode_root(), prop, &val)) | |
242 | return val; | |
243 | #endif | |
244 | return val_def; | |
245 | } | |
246 | ||
44ffb6f0 | 247 | /** |
07c9e683 SG |
248 | * smbios_add_prop_si() - Add a property from the devicetree or sysinfo |
249 | * | |
250 | * Sysinfo is used if available, with a fallback to devicetree | |
44ffb6f0 | 251 | * |
1e8989ad | 252 | * @ctx: context for writing the tables |
44ffb6f0 | 253 | * @prop: property to write |
b327e64d | 254 | * @sysinfo_id: unique identifier for the string value to be read |
a986ccea | 255 | * @dval: Default value to use if the string is not found or is empty |
8c6532d7 | 256 | * Return: 0 if not found, else SMBIOS string number (1 or more) |
44ffb6f0 | 257 | */ |
07c9e683 | 258 | static int smbios_add_prop_si(struct smbios_ctx *ctx, const char *prop, |
a986ccea | 259 | int sysinfo_id, const char *dval) |
44ffb6f0 | 260 | { |
738b3466 IA |
261 | int ret; |
262 | ||
a986ccea | 263 | if (!dval || !*dval) |
6ebf9136 | 264 | dval = NULL; |
a986ccea | 265 | |
07c9e683 SG |
266 | if (sysinfo_id && ctx->dev) { |
267 | char val[SMBIOS_STR_MAX]; | |
07c9e683 SG |
268 | |
269 | ret = sysinfo_get_str(ctx->dev, sysinfo_id, sizeof(val), val); | |
270 | if (!ret) | |
271 | return smbios_add_string(ctx, val); | |
272 | } | |
5d38e0d9 RM |
273 | if (!prop) |
274 | return smbios_add_string(ctx, dval); | |
275 | ||
e4f8e543 | 276 | if (IS_ENABLED(CONFIG_OF_CONTROL)) { |
738b3466 IA |
277 | const char *str = NULL; |
278 | char str_dt[128] = { 0 }; | |
279 | /* | |
280 | * If the node is not valid fallback and try the entire DT | |
281 | * so we can at least fill in manufacturer and board type | |
282 | */ | |
283 | if (ofnode_valid(ctx->node)) { | |
284 | str = ofnode_read_string(ctx->node, prop); | |
285 | } else { | |
286 | const struct map_sysinfo *nprop; | |
e4f8e543 | 287 | |
b6488caa | 288 | nprop = convert_sysinfo_to_dt(ctx->subnode_name, prop); |
738b3466 IA |
289 | get_str_from_dt(nprop, str_dt, sizeof(str_dt)); |
290 | str = (const char *)str_dt; | |
291 | } | |
a986ccea | 292 | |
738b3466 IA |
293 | ret = smbios_add_string(ctx, str && *str ? str : dval); |
294 | return ret; | |
e4f8e543 | 295 | } |
44ffb6f0 SG |
296 | |
297 | return 0; | |
298 | } | |
299 | ||
07c9e683 SG |
300 | /** |
301 | * smbios_add_prop() - Add a property from the devicetree | |
302 | * | |
a986ccea IA |
303 | * @prop: property to write. The default string will be written if |
304 | * prop is NULL | |
305 | * @dval: Default value to use if the string is not found or is empty | |
8c6532d7 | 306 | * Return: 0 if not found, else SMBIOS string number (1 or more) |
07c9e683 | 307 | */ |
a986ccea IA |
308 | static int smbios_add_prop(struct smbios_ctx *ctx, const char *prop, |
309 | const char *dval) | |
07c9e683 | 310 | { |
3fcbae5f | 311 | return smbios_add_prop_si(ctx, prop, SYSID_NONE, dval); |
07c9e683 SG |
312 | } |
313 | ||
0c95fff3 SG |
314 | static void smbios_set_eos(struct smbios_ctx *ctx, char *eos) |
315 | { | |
316 | ctx->eos = eos; | |
fd3b826d | 317 | ctx->next_ptr = eos; |
e9adaa75 SG |
318 | ctx->last_str = NULL; |
319 | } | |
320 | ||
321 | int smbios_update_version(const char *version) | |
322 | { | |
323 | char *ptr = gd->smbios_version; | |
324 | uint old_len, len; | |
325 | ||
326 | if (!ptr) | |
327 | return log_ret(-ENOENT); | |
328 | ||
329 | /* | |
330 | * This string is supposed to have at least enough bytes and is | |
331 | * padded with spaces. Update it, taking care not to move the | |
332 | * \0 terminator, so that other strings in the string table | |
333 | * are not disturbed. See smbios_add_string() | |
334 | */ | |
335 | old_len = strnlen(ptr, SMBIOS_STR_MAX); | |
336 | len = strnlen(version, SMBIOS_STR_MAX); | |
337 | if (len > old_len) | |
338 | return log_ret(-ENOSPC); | |
339 | ||
340 | log_debug("Replacing SMBIOS type 0 version string '%s'\n", ptr); | |
341 | memcpy(ptr, version, len); | |
342 | #ifdef LOG_DEBUG | |
343 | print_buffer((ulong)ptr, ptr, 1, old_len + 1, 0); | |
344 | #endif | |
345 | ||
346 | return 0; | |
0c95fff3 SG |
347 | } |
348 | ||
721e992a BM |
349 | /** |
350 | * smbios_string_table_len() - compute the string area size | |
351 | * | |
352 | * This computes the size of the string area including the string terminator. | |
353 | * | |
fd3b826d | 354 | * @ctx: SMBIOS context |
8c6532d7 | 355 | * Return: string area size |
721e992a | 356 | */ |
fd3b826d | 357 | static int smbios_string_table_len(const struct smbios_ctx *ctx) |
721e992a | 358 | { |
e31efe50 MB |
359 | /* In case no string is defined we have to return two \0 */ |
360 | if (ctx->next_ptr == ctx->eos) | |
361 | return 2; | |
362 | ||
fd3b826d SG |
363 | /* Allow for the final \0 after all strings */ |
364 | return (ctx->next_ptr + 1) - ctx->eos; | |
721e992a BM |
365 | } |
366 | ||
1e8989ad SG |
367 | static int smbios_write_type0(ulong *current, int handle, |
368 | struct smbios_ctx *ctx) | |
721e992a | 369 | { |
a2505fc8 | 370 | struct smbios_type0 *t; |
8aa5f8e0 | 371 | int len = sizeof(*t); |
721e992a | 372 | |
a2505fc8 | 373 | t = map_sysmem(*current, len); |
8aa5f8e0 | 374 | memset(t, 0, len); |
721e992a | 375 | fill_smbios_header(t, SMBIOS_BIOS_INFORMATION, len, handle); |
0c95fff3 | 376 | smbios_set_eos(ctx, t->eos); |
5d38e0d9 RM |
377 | t->vendor = smbios_add_prop_si(ctx, NULL, SYSID_SM_BIOS_VENDOR, |
378 | "U-Boot"); | |
e9adaa75 | 379 | |
5d38e0d9 RM |
380 | t->bios_ver = smbios_add_prop_si(ctx, "version", SYSID_SM_BIOS_VER, |
381 | PLAIN_VERSION); | |
e9adaa75 SG |
382 | if (t->bios_ver) |
383 | gd->smbios_version = ctx->last_str; | |
384 | log_debug("smbios_version = %p: '%s'\n", gd->smbios_version, | |
385 | gd->smbios_version); | |
386 | #ifdef LOG_DEBUG | |
387 | print_buffer((ulong)gd->smbios_version, gd->smbios_version, | |
388 | 1, strlen(gd->smbios_version) + 1, 0); | |
389 | #endif | |
5d38e0d9 RM |
390 | t->bios_release_date = smbios_add_prop_si(ctx, NULL, |
391 | SYSID_SM_BIOS_REL_DATE, | |
392 | U_BOOT_DMI_DATE); | |
e663b350 | 393 | #ifdef CONFIG_ROM_SIZE |
8c919fcd HS |
394 | if (CONFIG_ROM_SIZE < SZ_16M) { |
395 | t->bios_rom_size = (CONFIG_ROM_SIZE / 65536) - 1; | |
396 | } else { | |
397 | /* CONFIG_ROM_SIZE < 8 GiB */ | |
398 | t->bios_rom_size = 0xff; | |
399 | t->extended_bios_rom_size = CONFIG_ROM_SIZE >> 20; | |
400 | } | |
e663b350 | 401 | #endif |
721e992a BM |
402 | t->bios_characteristics = BIOS_CHARACTERISTICS_PCI_SUPPORTED | |
403 | BIOS_CHARACTERISTICS_SELECTABLE_BOOT | | |
404 | BIOS_CHARACTERISTICS_UPGRADEABLE; | |
405 | #ifdef CONFIG_GENERATE_ACPI_TABLE | |
406 | t->bios_characteristics_ext1 = BIOS_CHARACTERISTICS_EXT1_ACPI; | |
e663b350 AG |
407 | #endif |
408 | #ifdef CONFIG_EFI_LOADER | |
ff192304 | 409 | t->bios_characteristics_ext2 |= BIOS_CHARACTERISTICS_EXT2_UEFI; |
721e992a | 410 | #endif |
ff192304 | 411 | t->bios_characteristics_ext2 |= BIOS_CHARACTERISTICS_EXT2_TARGET; |
e663b350 | 412 | |
7617f996 SG |
413 | /* bios_major_release has only one byte, so drop century */ |
414 | t->bios_major_release = U_BOOT_VERSION_NUM % 100; | |
415 | t->bios_minor_release = U_BOOT_VERSION_NUM_PATCH; | |
721e992a BM |
416 | t->ec_major_release = 0xff; |
417 | t->ec_minor_release = 0xff; | |
418 | ||
8aa5f8e0 | 419 | len = t->hdr.length + smbios_string_table_len(ctx); |
721e992a | 420 | *current += len; |
a2505fc8 | 421 | unmap_sysmem(t); |
721e992a BM |
422 | |
423 | return len; | |
424 | } | |
425 | ||
1e8989ad SG |
426 | static int smbios_write_type1(ulong *current, int handle, |
427 | struct smbios_ctx *ctx) | |
721e992a | 428 | { |
a2505fc8 | 429 | struct smbios_type1 *t; |
8aa5f8e0 | 430 | int len = sizeof(*t); |
00caae6d | 431 | char *serial_str = env_get("serial#"); |
721e992a | 432 | |
a2505fc8 | 433 | t = map_sysmem(*current, len); |
8aa5f8e0 | 434 | memset(t, 0, len); |
721e992a | 435 | fill_smbios_header(t, SMBIOS_SYSTEM_INFORMATION, len, handle); |
0c95fff3 | 436 | smbios_set_eos(ctx, t->eos); |
5d38e0d9 | 437 | |
a5a57562 | 438 | t->manufacturer = smbios_add_prop_si(ctx, "manufacturer", |
3fcbae5f | 439 | SYSID_SM_SYSTEM_MANUFACTURER, |
a5a57562 MS |
440 | NULL); |
441 | t->product_name = smbios_add_prop_si(ctx, "product", | |
5d38e0d9 RM |
442 | SYSID_SM_SYSTEM_PRODUCT, NULL); |
443 | t->version = smbios_add_prop_si(ctx, "version", SYSID_SM_SYSTEM_VERSION, | |
6ebf9136 | 444 | NULL); |
6fb580d7 | 445 | if (serial_str) { |
a986ccea | 446 | t->serial_number = smbios_add_prop(ctx, NULL, serial_str); |
8aa5f8e0 | 447 | strlcpy((char *)t->uuid, serial_str, sizeof(t->uuid)); |
44ffb6f0 | 448 | } else { |
a5a57562 | 449 | t->serial_number = smbios_add_prop_si(ctx, "serial", |
3fcbae5f | 450 | SYSID_SM_SYSTEM_SERIAL, |
a5a57562 | 451 | NULL); |
6fb580d7 | 452 | } |
5d38e0d9 RM |
453 | t->wakeup_type = smbios_get_val_si(ctx, "wakeup-type", |
454 | SYSID_SM_SYSTEM_WAKEUP, | |
455 | SMBIOS_WAKEUP_TYPE_UNKNOWN); | |
456 | t->sku_number = smbios_add_prop_si(ctx, "sku", SYSID_SM_SYSTEM_SKU, | |
457 | NULL); | |
458 | t->family = smbios_add_prop_si(ctx, "family", SYSID_SM_SYSTEM_FAMILY, | |
459 | NULL); | |
721e992a | 460 | |
8aa5f8e0 | 461 | len = t->hdr.length + smbios_string_table_len(ctx); |
721e992a | 462 | *current += len; |
a2505fc8 | 463 | unmap_sysmem(t); |
721e992a BM |
464 | |
465 | return len; | |
466 | } | |
467 | ||
1e8989ad SG |
468 | static int smbios_write_type2(ulong *current, int handle, |
469 | struct smbios_ctx *ctx) | |
721e992a | 470 | { |
a2505fc8 | 471 | struct smbios_type2 *t; |
8aa5f8e0 | 472 | int len = sizeof(*t); |
5d38e0d9 | 473 | u8 *eos_addr; |
721e992a | 474 | |
5d38e0d9 RM |
475 | /* |
476 | * reserve the space for the dynamic bytes of contained object handles. | |
477 | * TODO: len += <obj_handle_num> * SMBIOS_TYPE2_CON_OBJ_HANDLE_SIZE | |
478 | * obj_handle_num can be from DT node "baseboard" or sysinfo driver. | |
479 | */ | |
a2505fc8 | 480 | t = map_sysmem(*current, len); |
8aa5f8e0 | 481 | memset(t, 0, len); |
721e992a | 482 | fill_smbios_header(t, SMBIOS_BOARD_INFORMATION, len, handle); |
5d38e0d9 RM |
483 | |
484 | /* eos is at the end of the structure */ | |
485 | eos_addr = (u8 *)t + len - sizeof(t->eos); | |
486 | smbios_set_eos(ctx, eos_addr); | |
487 | ||
a5a57562 | 488 | t->manufacturer = smbios_add_prop_si(ctx, "manufacturer", |
3fcbae5f | 489 | SYSID_SM_BASEBOARD_MANUFACTURER, |
a5a57562 MS |
490 | NULL); |
491 | t->product_name = smbios_add_prop_si(ctx, "product", | |
5d38e0d9 | 492 | SYSID_SM_BASEBOARD_PRODUCT, NULL); |
07c9e683 | 493 | t->version = smbios_add_prop_si(ctx, "version", |
5d38e0d9 | 494 | SYSID_SM_BASEBOARD_VERSION, NULL); |
a5a57562 | 495 | t->serial_number = smbios_add_prop_si(ctx, "serial", |
5d38e0d9 | 496 | SYSID_SM_BASEBOARD_SERIAL, NULL); |
a5a57562 | 497 | t->asset_tag_number = smbios_add_prop_si(ctx, "asset-tag", |
3fcbae5f | 498 | SYSID_SM_BASEBOARD_ASSET_TAG, |
a5a57562 | 499 | NULL); |
5d38e0d9 RM |
500 | t->feature_flags = smbios_get_val_si(ctx, "feature-flags", |
501 | SYSID_SM_BASEBOARD_FEATURE, 0); | |
502 | ||
503 | t->chassis_location = | |
504 | smbios_add_prop_si(ctx, "chassis-location", | |
505 | SYSID_SM_BASEBOARD_CHASSIS_LOCAT, NULL); | |
506 | t->board_type = smbios_get_val_si(ctx, "board-type", | |
507 | SYSID_SM_BASEBOARD_TYPE, | |
508 | SMBIOS_BOARD_TYPE_UNKNOWN); | |
509 | ||
510 | /* | |
511 | * TODO: | |
512 | * Populate the Contained Object Handles if they exist | |
513 | * t->number_contained_objects = <obj_handle_num>; | |
514 | */ | |
515 | ||
5778c88e | 516 | t->chassis_handle = handle + 1; |
721e992a | 517 | |
8aa5f8e0 | 518 | len = t->hdr.length + smbios_string_table_len(ctx); |
721e992a | 519 | *current += len; |
a2505fc8 | 520 | unmap_sysmem(t); |
721e992a BM |
521 | |
522 | return len; | |
523 | } | |
524 | ||
1e8989ad SG |
525 | static int smbios_write_type3(ulong *current, int handle, |
526 | struct smbios_ctx *ctx) | |
721e992a | 527 | { |
a2505fc8 | 528 | struct smbios_type3 *t; |
8aa5f8e0 | 529 | int len = sizeof(*t); |
5d38e0d9 RM |
530 | u8 *eos_addr; |
531 | size_t elem_size = 0; | |
bcf456dd RM |
532 | __maybe_unused u8 *elem_addr; |
533 | __maybe_unused u8 *sku_num_addr; | |
5d38e0d9 RM |
534 | |
535 | /* | |
536 | * reserve the space for the dynamic bytes of contained elements. | |
537 | * TODO: elem_size = <element_count> * <element_record_length> | |
538 | * element_count and element_record_length can be from DT node | |
539 | * "chassis" or sysinfo driver. | |
540 | */ | |
541 | len += elem_size; | |
721e992a | 542 | |
a2505fc8 | 543 | t = map_sysmem(*current, len); |
8aa5f8e0 | 544 | memset(t, 0, len); |
721e992a | 545 | fill_smbios_header(t, SMBIOS_SYSTEM_ENCLOSURE, len, handle); |
bcf456dd RM |
546 | #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) |
547 | elem_addr = (u8 *)t + offsetof(struct smbios_type3, sku_number); | |
548 | sku_num_addr = elem_addr + elem_size; | |
549 | #endif | |
5d38e0d9 RM |
550 | /* eos is at the end of the structure */ |
551 | eos_addr = (u8 *)t + len - sizeof(t->eos); | |
552 | smbios_set_eos(ctx, eos_addr); | |
553 | ||
554 | t->manufacturer = smbios_add_prop_si(ctx, "manufacturer", | |
555 | SYSID_SM_ENCLOSURE_MANUFACTURER, | |
556 | NULL); | |
557 | t->chassis_type = smbios_get_val_si(ctx, "chassis-type", | |
558 | SYSID_SM_ENCLOSURE_TYPE, | |
559 | SMBIOS_ENCLOSURE_UNKNOWN); | |
560 | t->bootup_state = smbios_get_val_si(ctx, "bootup-state", | |
561 | SYSID_SM_ENCLOSURE_BOOTUP, | |
562 | SMBIOS_STATE_UNKNOWN); | |
563 | t->power_supply_state = smbios_get_val_si(ctx, "power-supply-state", | |
564 | SYSID_SM_ENCLOSURE_POW, | |
565 | SMBIOS_STATE_UNKNOWN); | |
566 | t->thermal_state = smbios_get_val_si(ctx, "thermal-state", | |
567 | SYSID_SM_ENCLOSURE_THERMAL, | |
568 | SMBIOS_STATE_UNKNOWN); | |
569 | t->security_status = smbios_get_val_si(ctx, "security-status", | |
570 | SYSID_SM_ENCLOSURE_SECURITY, | |
571 | SMBIOS_SECURITY_UNKNOWN); | |
721e992a | 572 | |
bcf456dd RM |
573 | #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) |
574 | t->version = smbios_add_prop_si(ctx, "version", | |
575 | SYSID_SM_ENCLOSURE_VERSION, NULL); | |
576 | t->serial_number = smbios_add_prop_si(ctx, "serial", | |
577 | SYSID_SM_ENCLOSURE_SERIAL, NULL); | |
578 | t->asset_tag_number = smbios_add_prop_si(ctx, "asset-tag", | |
579 | SYSID_SM_BASEBOARD_ASSET_TAG, | |
580 | NULL); | |
581 | t->oem_defined = smbios_get_val_si(ctx, "oem-defined", | |
582 | SYSID_SM_ENCLOSURE_OEM, 0); | |
583 | t->height = smbios_get_val_si(ctx, "height", | |
584 | SYSID_SM_ENCLOSURE_HEIGHT, 0); | |
585 | t->number_of_power_cords = | |
586 | smbios_get_val_si(ctx, "number-of-power-cords", | |
587 | SYSID_SM_ENCLOSURE_POWCORE_NUM, 0); | |
588 | ||
589 | /* | |
590 | * TODO: Populate the Contained Element Record if they exist | |
591 | * t->element_count = <element_num>; | |
592 | * t->element_record_length = <element_len>; | |
593 | */ | |
594 | ||
595 | *sku_num_addr = smbios_add_prop_si(ctx, "sku", SYSID_SM_ENCLOSURE_SKU, | |
596 | NULL); | |
597 | #endif | |
598 | ||
8aa5f8e0 | 599 | len = t->hdr.length + smbios_string_table_len(ctx); |
721e992a | 600 | *current += len; |
a2505fc8 | 601 | unmap_sysmem(t); |
721e992a BM |
602 | |
603 | return len; | |
604 | } | |
605 | ||
1e8989ad SG |
606 | static void smbios_write_type4_dm(struct smbios_type4 *t, |
607 | struct smbios_ctx *ctx) | |
96476206 AG |
608 | { |
609 | u16 processor_family = SMBIOS_PROCESSOR_FAMILY_UNKNOWN; | |
6ebf9136 HS |
610 | const char *vendor = NULL; |
611 | const char *name = NULL; | |
bcf456dd RM |
612 | __maybe_unused void *id_data = NULL; |
613 | __maybe_unused size_t id_size = 0; | |
96476206 AG |
614 | |
615 | #ifdef CONFIG_CPU | |
616 | char processor_name[49]; | |
617 | char vendor_name[49]; | |
78227d4e | 618 | struct udevice *cpu = NULL; |
96476206 | 619 | |
78227d4e SG |
620 | uclass_find_first_device(UCLASS_CPU, &cpu); |
621 | if (cpu) { | |
8a8d24bd | 622 | struct cpu_plat *plat = dev_get_parent_plat(cpu); |
96476206 AG |
623 | |
624 | if (plat->family) | |
625 | processor_family = plat->family; | |
626 | t->processor_id[0] = plat->id[0]; | |
627 | t->processor_id[1] = plat->id[1]; | |
628 | ||
78227d4e | 629 | if (!cpu_get_vendor(cpu, vendor_name, sizeof(vendor_name))) |
96476206 | 630 | vendor = vendor_name; |
78227d4e | 631 | if (!cpu_get_desc(cpu, processor_name, sizeof(processor_name))) |
96476206 AG |
632 | name = processor_name; |
633 | } | |
634 | #endif | |
5d38e0d9 RM |
635 | if (processor_family == SMBIOS_PROCESSOR_FAMILY_UNKNOWN) |
636 | processor_family = | |
637 | smbios_get_val_si(ctx, "family", | |
638 | SYSID_SM_PROCESSOR_FAMILY, | |
639 | SMBIOS_PROCESSOR_FAMILY_UNKNOWN); | |
640 | ||
641 | if (processor_family == SMBIOS_PROCESSOR_FAMILY_EXT) | |
642 | t->processor_family2 = | |
643 | smbios_get_val_si(ctx, "family2", | |
644 | SYSID_SM_PROCESSOR_FAMILY2, | |
645 | SMBIOS_PROCESSOR_FAMILY_UNKNOWN); | |
646 | ||
647 | t->processor_family = processor_family; | |
648 | t->processor_manufacturer = | |
649 | smbios_add_prop_si(ctx, "manufacturer", | |
650 | SYSID_SM_PROCESSOR_MANUFACT, vendor); | |
651 | t->processor_version = smbios_add_prop_si(ctx, "version", | |
652 | SYSID_SM_PROCESSOR_VERSION, | |
653 | name); | |
bcf456dd RM |
654 | |
655 | #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) | |
656 | if (t->processor_id[0] || t->processor_id[1] || | |
657 | sysinfo_get_data(ctx->dev, SYSID_SM_PROCESSOR_ID, &id_data, | |
658 | &id_size)) | |
659 | return; | |
96476206 | 660 | |
bcf456dd RM |
661 | if (id_data && id_size == sizeof(t->processor_id)) |
662 | memcpy((u8 *)t->processor_id, id_data, id_size); | |
663 | #endif | |
96476206 AG |
664 | } |
665 | ||
1e8989ad SG |
666 | static int smbios_write_type4(ulong *current, int handle, |
667 | struct smbios_ctx *ctx) | |
721e992a | 668 | { |
a2505fc8 | 669 | struct smbios_type4 *t; |
8aa5f8e0 | 670 | int len = sizeof(*t); |
bcf456dd RM |
671 | __maybe_unused void *hdl; |
672 | __maybe_unused size_t hdl_size; | |
721e992a | 673 | |
a2505fc8 | 674 | t = map_sysmem(*current, len); |
8aa5f8e0 | 675 | memset(t, 0, len); |
721e992a | 676 | fill_smbios_header(t, SMBIOS_PROCESSOR_INFORMATION, len, handle); |
0c95fff3 | 677 | smbios_set_eos(ctx, t->eos); |
5d38e0d9 RM |
678 | t->socket_design = smbios_add_prop_si(ctx, "socket-design", |
679 | SYSID_SM_PROCESSOR_SOCKET, NULL); | |
680 | t->processor_type = smbios_get_val_si(ctx, "processor-type", | |
681 | SYSID_SM_PROCESSOR_TYPE, | |
682 | SMBIOS_PROCESSOR_TYPE_UNKNOWN); | |
1e8989ad | 683 | smbios_write_type4_dm(t, ctx); |
721e992a | 684 | |
5d38e0d9 RM |
685 | t->status = smbios_get_val_si(ctx, "processor-status", |
686 | SYSID_SM_PROCESSOR_STATUS, | |
687 | SMBIOS_PROCESSOR_STATUS_UNKNOWN); | |
688 | t->processor_upgrade = | |
689 | smbios_get_val_si(ctx, "upgrade", SYSID_SM_PROCESSOR_UPGRADE, | |
690 | SMBIOS_PROCESSOR_UPGRADE_UNKNOWN); | |
691 | ||
692 | t->l1_cache_handle = SMBIOS_CACHE_HANDLE_NONE; | |
693 | t->l2_cache_handle = SMBIOS_CACHE_HANDLE_NONE; | |
694 | t->l3_cache_handle = SMBIOS_CACHE_HANDLE_NONE; | |
721e992a | 695 | |
bcf456dd RM |
696 | #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) |
697 | t->voltage = smbios_get_val_si(ctx, "voltage", | |
698 | SYSID_SM_PROCESSOR_VOLTAGE, 0); | |
699 | t->external_clock = smbios_get_val_si(ctx, "external-clock", | |
700 | SYSID_SM_PROCESSOR_EXT_CLOCK, 0); | |
701 | t->max_speed = smbios_get_val_si(ctx, "max-speed", | |
702 | SYSID_SM_PROCESSOR_MAX_SPEED, 0); | |
703 | t->current_speed = smbios_get_val_si(ctx, "current-speed", | |
704 | SYSID_SM_PROCESSOR_CUR_SPEED, 0); | |
705 | ||
706 | /* Read the cache handles */ | |
707 | if (!sysinfo_get_data(ctx->dev, SYSID_SM_CACHE_HANDLE, &hdl, | |
708 | &hdl_size) && | |
709 | (hdl_size == SYSINFO_CACHE_LVL_MAX * sizeof(u16))) { | |
710 | u16 *handle = (u16 *)hdl; | |
711 | ||
712 | if (*handle) | |
713 | t->l1_cache_handle = *handle; | |
714 | ||
715 | handle++; | |
716 | if (*handle) | |
717 | t->l2_cache_handle = *handle; | |
718 | ||
719 | handle++; | |
720 | if (*handle) | |
721 | t->l3_cache_handle = *handle; | |
722 | } | |
723 | ||
724 | t->serial_number = smbios_add_prop_si(ctx, "serial", | |
725 | SYSID_SM_PROCESSOR_SN, NULL); | |
726 | t->asset_tag = smbios_add_prop_si(ctx, "asset-tag", | |
727 | SYSID_SM_PROCESSOR_ASSET_TAG, NULL); | |
728 | t->part_number = smbios_add_prop_si(ctx, "part-number", | |
729 | SYSID_SM_PROCESSOR_PN, NULL); | |
730 | t->core_count = smbios_get_val_si(ctx, "core-count", | |
731 | SYSID_SM_PROCESSOR_CORE_CNT, 0); | |
732 | t->core_enabled = smbios_get_val_si(ctx, "core-enabled", | |
733 | SYSID_SM_PROCESSOR_CORE_EN, 0); | |
734 | t->thread_count = smbios_get_val_si(ctx, "thread-count", | |
735 | SYSID_SM_PROCESSOR_THREAD_CNT, 0); | |
736 | t->processor_characteristics = | |
737 | smbios_get_val_si(ctx, "characteristics", | |
738 | SYSID_SM_PROCESSOR_CHARA, | |
739 | SMBIOS_PROCESSOR_UND); | |
740 | t->core_count2 = smbios_get_val_si(ctx, "core-count2", | |
741 | SYSID_SM_PROCESSOR_CORE_CNT2, 0); | |
742 | t->core_enabled2 = smbios_get_val_si(ctx, "core-enabled2", | |
743 | SYSID_SM_PROCESSOR_CORE_EN2, 0); | |
744 | t->thread_count2 = smbios_get_val_si(ctx, "thread-count2", | |
745 | SYSID_SM_PROCESSOR_THREAD_CNT2, 0); | |
746 | t->thread_enabled = smbios_get_val_si(ctx, "thread-enabled", | |
747 | SYSID_SM_PROCESSOR_THREAD_EN, 0); | |
748 | #endif | |
749 | ||
750 | len = t->hdr.length + smbios_string_table_len(ctx); | |
751 | *current += len; | |
752 | unmap_sysmem(t); | |
753 | ||
754 | return len; | |
755 | } | |
756 | ||
757 | #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) | |
758 | ||
759 | static int smbios_write_type7_1level(ulong *current, int handle, | |
760 | struct smbios_ctx *ctx, int level) | |
761 | { | |
762 | struct smbios_type7 *t; | |
763 | int len = sizeof(*t); | |
764 | void *hdl; | |
765 | size_t hdl_size; | |
766 | ||
767 | t = map_sysmem(*current, len); | |
768 | memset(t, 0, len); | |
769 | fill_smbios_header(t, SMBIOS_CACHE_INFORMATION, len, handle); | |
770 | smbios_set_eos(ctx, t->eos); | |
771 | ||
772 | t->socket_design = smbios_add_prop_si(ctx, "socket-design", | |
773 | SYSID_SM_CACHE_SOCKET + level, | |
774 | NULL); | |
775 | t->config.data = smbios_get_val_si(ctx, "config", | |
776 | SYSID_SM_CACHE_CONFIG + level, | |
777 | (level - 1) | SMBIOS_CACHE_OP_UND); | |
778 | t->max_size.data = smbios_get_val_si(ctx, "max-size", | |
779 | SYSID_SM_CACHE_MAX_SIZE + level, | |
780 | 0); | |
781 | t->inst_size.data = smbios_get_val_si(ctx, "installed-size", | |
782 | SYSID_SM_CACHE_INST_SIZE + level, | |
783 | 0); | |
784 | t->supp_sram_type.data = | |
785 | smbios_get_val_si(ctx, "supported-sram-type", | |
786 | SYSID_SM_CACHE_SUPSRAM_TYPE + level, | |
787 | SMBIOS_CACHE_SRAM_TYPE_UNKNOWN); | |
788 | t->curr_sram_type.data = | |
789 | smbios_get_val_si(ctx, "current-sram-type", | |
790 | SYSID_SM_CACHE_CURSRAM_TYPE + level, | |
791 | SMBIOS_CACHE_SRAM_TYPE_UNKNOWN); | |
792 | t->speed = smbios_get_val_si(ctx, "speed", SYSID_SM_CACHE_SPEED + level, | |
793 | 0); | |
794 | t->err_corr_type = smbios_get_val_si(ctx, "error-correction-type", | |
795 | SYSID_SM_CACHE_ERRCOR_TYPE + level, | |
796 | SMBIOS_CACHE_ERRCORR_UNKNOWN); | |
797 | t->sys_cache_type = | |
798 | smbios_get_val_si(ctx, "system-cache-type", | |
799 | SYSID_SM_CACHE_SCACHE_TYPE + level, | |
800 | SMBIOS_CACHE_SYSCACHE_TYPE_UNKNOWN); | |
801 | t->associativity = smbios_get_val_si(ctx, "associativity", | |
802 | SYSID_SM_CACHE_ASSOC + level, | |
803 | SMBIOS_CACHE_ASSOC_UNKNOWN); | |
804 | t->max_size2.data = smbios_get_val_si(ctx, "max-size2", | |
805 | SYSID_SM_CACHE_MAX_SIZE2 + level, | |
806 | 0); | |
807 | t->inst_size2.data = | |
808 | smbios_get_val_si(ctx, "installed-size2", | |
809 | SYSID_SM_CACHE_INST_SIZE2 + level, 0); | |
810 | ||
811 | /* Save the cache handles */ | |
812 | if (!sysinfo_get_data(ctx->dev, SYSID_SM_CACHE_HANDLE, &hdl, | |
813 | &hdl_size)) { | |
814 | if (hdl_size == SYSINFO_CACHE_LVL_MAX * sizeof(u16)) | |
815 | *((u16 *)hdl + level) = handle; | |
816 | } | |
817 | ||
8aa5f8e0 | 818 | len = t->hdr.length + smbios_string_table_len(ctx); |
721e992a | 819 | *current += len; |
a2505fc8 | 820 | unmap_sysmem(t); |
721e992a BM |
821 | |
822 | return len; | |
823 | } | |
824 | ||
bcf456dd RM |
825 | static int smbios_write_type7(ulong *current, int handle, |
826 | struct smbios_ctx *ctx) | |
827 | { | |
828 | int len = 0; | |
829 | int i, level; | |
830 | ofnode parent = ctx->node; | |
831 | struct smbios_ctx ctx_bak; | |
832 | ||
833 | memcpy(&ctx_bak, ctx, sizeof(ctx_bak)); | |
834 | ||
835 | /* Get the number of level */ | |
836 | level = smbios_get_val_si(ctx, NULL, SYSID_SM_CACHE_LEVEL, 0); | |
837 | if (level >= SYSINFO_CACHE_LVL_MAX) /* Error, return 0-length */ | |
838 | return 0; | |
839 | ||
840 | for (i = 0; i <= level; i++) { | |
841 | char buf[9] = ""; | |
842 | ||
843 | if (!snprintf(buf, sizeof(buf), "l%d-cache", i + 1)) | |
844 | return 0; | |
845 | ctx->subnode_name = buf; | |
846 | ctx->node = ofnode_find_subnode(parent, ctx->subnode_name); | |
847 | len += smbios_write_type7_1level(current, handle++, ctx, i); | |
848 | memcpy(ctx, &ctx_bak, sizeof(*ctx)); | |
849 | } | |
850 | return len; | |
851 | } | |
852 | ||
853 | #endif /* #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) */ | |
854 | ||
1e8989ad SG |
855 | static int smbios_write_type32(ulong *current, int handle, |
856 | struct smbios_ctx *ctx) | |
721e992a | 857 | { |
a2505fc8 | 858 | struct smbios_type32 *t; |
8aa5f8e0 | 859 | int len = sizeof(*t); |
721e992a | 860 | |
a2505fc8 | 861 | t = map_sysmem(*current, len); |
8aa5f8e0 | 862 | memset(t, 0, len); |
721e992a | 863 | fill_smbios_header(t, SMBIOS_SYSTEM_BOOT_INFORMATION, len, handle); |
0c95fff3 | 864 | smbios_set_eos(ctx, t->eos); |
721e992a BM |
865 | |
866 | *current += len; | |
a2505fc8 | 867 | unmap_sysmem(t); |
721e992a BM |
868 | |
869 | return len; | |
870 | } | |
871 | ||
1e8989ad SG |
872 | static int smbios_write_type127(ulong *current, int handle, |
873 | struct smbios_ctx *ctx) | |
721e992a | 874 | { |
a2505fc8 | 875 | struct smbios_type127 *t; |
8aa5f8e0 | 876 | int len = sizeof(*t); |
721e992a | 877 | |
a2505fc8 | 878 | t = map_sysmem(*current, len); |
8aa5f8e0 | 879 | memset(t, 0, len); |
721e992a BM |
880 | fill_smbios_header(t, SMBIOS_END_OF_TABLE, len, handle); |
881 | ||
882 | *current += len; | |
a2505fc8 | 883 | unmap_sysmem(t); |
721e992a BM |
884 | |
885 | return len; | |
886 | } | |
887 | ||
44ffb6f0 | 888 | static struct smbios_write_method smbios_write_funcs[] = { |
e9adaa75 | 889 | { smbios_write_type0, "bios", }, |
44ffb6f0 SG |
890 | { smbios_write_type1, "system", }, |
891 | { smbios_write_type2, "baseboard", }, | |
5778c88e | 892 | /* Type 3 must immediately follow type 2 due to chassis handle. */ |
44ffb6f0 | 893 | { smbios_write_type3, "chassis", }, |
bcf456dd RM |
894 | #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) |
895 | /* Type 7 must ahead of type 4 to get cache handles. */ | |
896 | { smbios_write_type7, "cache", }, | |
897 | #endif | |
5d38e0d9 | 898 | { smbios_write_type4, "processor"}, |
44ffb6f0 SG |
899 | { smbios_write_type32, }, |
900 | { smbios_write_type127 }, | |
721e992a BM |
901 | }; |
902 | ||
42fd8c19 | 903 | ulong write_smbios_table(ulong addr) |
721e992a | 904 | { |
44ffb6f0 | 905 | ofnode parent_node = ofnode_null(); |
f19cf8d4 | 906 | ulong table_addr, start_addr; |
1c5f6fa3 | 907 | struct smbios3_entry *se; |
1e8989ad | 908 | struct smbios_ctx ctx; |
42fd8c19 | 909 | ulong tables; |
721e992a | 910 | int len = 0; |
721e992a | 911 | int handle = 0; |
721e992a BM |
912 | int i; |
913 | ||
1e8989ad | 914 | ctx.node = ofnode_null(); |
5d38e0d9 | 915 | if (CONFIG_IS_ENABLED(SYSINFO)) { |
1e8989ad | 916 | uclass_first_device(UCLASS_SYSINFO, &ctx.dev); |
85df7f17 MS |
917 | if (ctx.dev) { |
918 | int ret; | |
919 | ||
1e8989ad | 920 | parent_node = dev_read_subnode(ctx.dev, "smbios"); |
85df7f17 | 921 | ret = sysinfo_detect(ctx.dev); |
4a8a54c3 SG |
922 | |
923 | /* | |
924 | * ignore the error since many boards don't implement | |
925 | * this and we can still use the info in the devicetree | |
926 | */ | |
927 | ret = log_msg_ret("sys", ret); | |
85df7f17 | 928 | } |
1e8989ad SG |
929 | } else { |
930 | ctx.dev = NULL; | |
78227d4e SG |
931 | } |
932 | ||
f19cf8d4 | 933 | start_addr = addr; |
721e992a | 934 | |
1c5f6fa3 SG |
935 | /* move past the (so-far-unwritten) header to start writing structs */ |
936 | addr = ALIGN(addr + sizeof(struct smbios3_entry), 16); | |
721e992a BM |
937 | tables = addr; |
938 | ||
939 | /* populate minimum required tables */ | |
940 | for (i = 0; i < ARRAY_SIZE(smbios_write_funcs); i++) { | |
44ffb6f0 | 941 | const struct smbios_write_method *method; |
44ffb6f0 SG |
942 | |
943 | method = &smbios_write_funcs[i]; | |
b6488caa IA |
944 | ctx.subnode_name = NULL; |
945 | if (method->subnode_name) { | |
946 | ctx.subnode_name = method->subnode_name; | |
947 | if (IS_ENABLED(CONFIG_OF_CONTROL)) | |
948 | ctx.node = ofnode_find_subnode(parent_node, | |
949 | method->subnode_name); | |
950 | } | |
e494258d | 951 | len += method->write((ulong *)&addr, handle++, &ctx); |
721e992a BM |
952 | } |
953 | ||
a2505fc8 SG |
954 | /* |
955 | * We must use a pointer here so things work correctly on sandbox. The | |
956 | * user of this table is not aware of the mapping of addresses to | |
957 | * sandbox's DRAM buffer. | |
958 | */ | |
959 | table_addr = (ulong)map_sysmem(tables, 0); | |
1c5f6fa3 SG |
960 | |
961 | /* now go back and write the SMBIOS3 header */ | |
ccefbf32 HS |
962 | se = map_sysmem(start_addr, sizeof(struct smbios3_entry)); |
963 | memset(se, '\0', sizeof(struct smbios3_entry)); | |
1c5f6fa3 SG |
964 | memcpy(se->anchor, "_SM3_", 5); |
965 | se->length = sizeof(struct smbios3_entry); | |
966 | se->major_ver = SMBIOS_MAJOR_VER; | |
967 | se->minor_ver = SMBIOS_MINOR_VER; | |
968 | se->doc_rev = 0; | |
969 | se->entry_point_rev = 1; | |
406c410e | 970 | se->table_maximum_size = len; |
1c5f6fa3 SG |
971 | se->struct_table_address = table_addr; |
972 | se->checksum = table_compute_checksum(se, sizeof(struct smbios3_entry)); | |
973 | unmap_sysmem(se); | |
721e992a BM |
974 | |
975 | return addr; | |
976 | } |