1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
4  *
5  * Adapted from coreboot src/arch/x86/smbios.c
6  */
7 
8 #define LOG_CATEGORY	LOGC_BOARD
9 
10 #include <display_options.h>
11 #include <dm.h>
12 #include <env.h>
13 #include <linux/stringify.h>
14 #include <linux/string.h>
15 #include <mapmem.h>
16 #include <smbios.h>
17 #include <sysinfo.h>
18 #include <tables_csum.h>
19 #include <version.h>
20 #include <malloc.h>
21 #include <dm/ofnode.h>
22 #ifdef CONFIG_CPU
23 #include <cpu.h>
24 #include <dm/uclass-internal.h>
25 #endif
26 #include <linux/sizes.h>
27 
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 
50 DECLARE_GLOBAL_DATA_PTR;
51 
52 /**
53  * struct map_sysinfo - Mapping of sysinfo strings to DT
54  *
55  * @si_str: sysinfo string
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 {
61 	const char *si_node;
62 	const char *si_str;
63 	const char *dt_str;
64 	int max;
65 };
66 
67 static const struct map_sysinfo sysinfo_to_dt[] = {
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 },
72 };
73 
74 /**
75  * struct smbios_ctx - context for writing SMBIOS tables
76  *
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
88  */
89 struct smbios_ctx {
90 	ofnode node;
91 	struct udevice *dev;
92 	const char *subnode_name;
93 	char *eos;
94 	char *next_ptr;
95 	char *last_str;
96 };
97 
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
103  * @ctx:	context for writing the tables
104  * Return:	size of the structure
105  */
106 typedef int (*smbios_write_type)(ulong *addr, int handle,
107 				 struct smbios_ctx *ctx);
108 
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 
convert_sysinfo_to_dt(const char * node,const char * si)121 static const struct map_sysinfo *convert_sysinfo_to_dt(const char *node, const char *si)
122 {
123 	int i;
124 
125 	for (i = 0; i < ARRAY_SIZE(sysinfo_to_dt); i++) {
126 		if (node && !strcmp(node, sysinfo_to_dt[i].si_node) &&
127 		    !strcmp(si, sysinfo_to_dt[i].si_str))
128 			return &sysinfo_to_dt[i];
129 	}
130 
131 	return NULL;
132 }
133 
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  *
140  * @ctx:	SMBIOS context
141  * @str:	string to add
142  * Return:	string number in the string area. 0 if str is NULL.
143  */
smbios_add_string(struct smbios_ctx * ctx,const char * str)144 static int smbios_add_string(struct smbios_ctx *ctx, const char *str)
145 {
146 	int i = 1;
147 	char *p = ctx->eos;
148 
149 	if (!str)
150 		return 0;
151 
152 	for (;;) {
153 		if (!*p) {
154 			ctx->last_str = p;
155 			strcpy(p, str);
156 			p += strlen(str);
157 			*p++ = '\0';
158 			ctx->next_ptr = p;
159 			*p++ = '\0';
160 
161 			return i;
162 		}
163 
164 		if (!strcmp(p, str)) {
165 			ctx->last_str = p;
166 			return i;
167 		}
168 
169 		p += strlen(p) + 1;
170 		i++;
171 	}
172 }
173 
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
get_str_from_dt(const struct map_sysinfo * nprop,char * str,size_t size)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 
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  */
smbios_get_val_si(struct smbios_ctx * __maybe_unused ctx,const char * __maybe_unused prop,int __maybe_unused sysinfo_id,int val_def)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 
247 /**
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
251  *
252  * @ctx:	context for writing the tables
253  * @prop:	property to write
254  * @sysinfo_id: unique identifier for the string value to be read
255  * @dval:	Default value to use if the string is not found or is empty
256  * Return:	0 if not found, else SMBIOS string number (1 or more)
257  */
smbios_add_prop_si(struct smbios_ctx * ctx,const char * prop,int sysinfo_id,const char * dval)258 static int smbios_add_prop_si(struct smbios_ctx *ctx, const char *prop,
259 			      int sysinfo_id, const char *dval)
260 {
261 	int ret;
262 
263 	if (!dval || !*dval)
264 		dval = NULL;
265 
266 	if (sysinfo_id && ctx->dev) {
267 		char val[SMBIOS_STR_MAX];
268 
269 		ret = sysinfo_get_str(ctx->dev, sysinfo_id, sizeof(val), val);
270 		if (!ret)
271 			return smbios_add_string(ctx, val);
272 	}
273 	if (!prop)
274 		return smbios_add_string(ctx, dval);
275 
276 	if (IS_ENABLED(CONFIG_OF_CONTROL)) {
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;
287 
288 			nprop = convert_sysinfo_to_dt(ctx->subnode_name, prop);
289 			get_str_from_dt(nprop, str_dt, sizeof(str_dt));
290 			str = (const char *)str_dt;
291 		}
292 
293 		ret = smbios_add_string(ctx, str && *str ? str : dval);
294 		return ret;
295 	}
296 
297 	return 0;
298 }
299 
300 /**
301  * smbios_add_prop() - Add a property from the devicetree
302  *
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
306  * Return:	0 if not found, else SMBIOS string number (1 or more)
307  */
smbios_add_prop(struct smbios_ctx * ctx,const char * prop,const char * dval)308 static int smbios_add_prop(struct smbios_ctx *ctx, const char *prop,
309 			   const char *dval)
310 {
311 	return smbios_add_prop_si(ctx, prop, SYSID_NONE, dval);
312 }
313 
smbios_set_eos(struct smbios_ctx * ctx,char * eos)314 static void smbios_set_eos(struct smbios_ctx *ctx, char *eos)
315 {
316 	ctx->eos = eos;
317 	ctx->next_ptr = eos;
318 	ctx->last_str = NULL;
319 }
320 
smbios_update_version(const char * version)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;
347 }
348 
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  *
354  * @ctx:	SMBIOS context
355  * Return:	string area size
356  */
smbios_string_table_len(const struct smbios_ctx * ctx)357 static int smbios_string_table_len(const struct smbios_ctx *ctx)
358 {
359 	/* In case no string is defined we have to return two \0 */
360 	if (ctx->next_ptr == ctx->eos)
361 		return 2;
362 
363 	/* Allow for the final \0 after all strings */
364 	return (ctx->next_ptr + 1) - ctx->eos;
365 }
366 
smbios_write_type0(ulong * current,int handle,struct smbios_ctx * ctx)367 static int smbios_write_type0(ulong *current, int handle,
368 			      struct smbios_ctx *ctx)
369 {
370 	struct smbios_type0 *t;
371 	int len = sizeof(*t);
372 
373 	t = map_sysmem(*current, len);
374 	memset(t, 0, len);
375 	fill_smbios_header(t, SMBIOS_BIOS_INFORMATION, len, handle);
376 	smbios_set_eos(ctx, t->eos);
377 	t->vendor = smbios_add_prop_si(ctx, NULL, SYSID_SM_BIOS_VENDOR,
378 				       "U-Boot");
379 
380 	t->bios_ver = smbios_add_prop_si(ctx, "version", SYSID_SM_BIOS_VER,
381 					 PLAIN_VERSION);
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
390 	t->bios_release_date = smbios_add_prop_si(ctx, NULL,
391 						  SYSID_SM_BIOS_REL_DATE,
392 						  U_BOOT_DMI_DATE);
393 #ifdef CONFIG_ROM_SIZE
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 	}
401 #endif
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;
407 #endif
408 #ifdef CONFIG_EFI_LOADER
409 	t->bios_characteristics_ext2 |= BIOS_CHARACTERISTICS_EXT2_UEFI;
410 #endif
411 	t->bios_characteristics_ext2 |= BIOS_CHARACTERISTICS_EXT2_TARGET;
412 
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;
416 	t->ec_major_release = 0xff;
417 	t->ec_minor_release = 0xff;
418 
419 	len = t->hdr.length + smbios_string_table_len(ctx);
420 	*current += len;
421 	unmap_sysmem(t);
422 
423 	return len;
424 }
425 
smbios_write_type1(ulong * current,int handle,struct smbios_ctx * ctx)426 static int smbios_write_type1(ulong *current, int handle,
427 			      struct smbios_ctx *ctx)
428 {
429 	struct smbios_type1 *t;
430 	int len = sizeof(*t);
431 	char *serial_str = env_get("serial#");
432 	size_t uuid_len;
433 	void *uuid;
434 
435 	t = map_sysmem(*current, len);
436 	memset(t, 0, len);
437 	fill_smbios_header(t, SMBIOS_SYSTEM_INFORMATION, len, handle);
438 	smbios_set_eos(ctx, t->eos);
439 
440 	t->manufacturer = smbios_add_prop_si(ctx, "manufacturer",
441 					     SYSID_SM_SYSTEM_MANUFACTURER,
442 					     NULL);
443 	t->product_name = smbios_add_prop_si(ctx, "product",
444 					     SYSID_SM_SYSTEM_PRODUCT, NULL);
445 	t->version = smbios_add_prop_si(ctx, "version",	SYSID_SM_SYSTEM_VERSION,
446 					NULL);
447 	if (serial_str) {
448 		t->serial_number = smbios_add_prop(ctx, NULL, serial_str);
449 		strlcpy((char *)t->uuid, serial_str, sizeof(t->uuid));
450 	} else {
451 		t->serial_number = smbios_add_prop_si(ctx, "serial",
452 						      SYSID_SM_SYSTEM_SERIAL,
453 						      NULL);
454 	}
455 	if (!sysinfo_get_data(ctx->dev, SYSID_SM_SYSTEM_UUID, &uuid,
456 			      &uuid_len) &&
457 	    uuid_len == sizeof(t->uuid))
458 		memcpy(t->uuid, uuid, sizeof(t->uuid));
459 	t->wakeup_type = smbios_get_val_si(ctx, "wakeup-type",
460 					   SYSID_SM_SYSTEM_WAKEUP,
461 					   SMBIOS_WAKEUP_TYPE_UNKNOWN);
462 	t->sku_number = smbios_add_prop_si(ctx, "sku", SYSID_SM_SYSTEM_SKU,
463 					   NULL);
464 	t->family = smbios_add_prop_si(ctx, "family", SYSID_SM_SYSTEM_FAMILY,
465 				       NULL);
466 
467 	len = t->hdr.length + smbios_string_table_len(ctx);
468 	*current += len;
469 	unmap_sysmem(t);
470 
471 	return len;
472 }
473 
smbios_write_type2(ulong * current,int handle,struct smbios_ctx * ctx)474 static int smbios_write_type2(ulong *current, int handle,
475 			      struct smbios_ctx *ctx)
476 {
477 	struct smbios_type2 *t;
478 	int len = sizeof(*t);
479 	u8 *eos_addr;
480 
481 	/*
482 	 * reserve the space for the dynamic bytes of contained object handles.
483 	 * TODO: len += <obj_handle_num> * SMBIOS_TYPE2_CON_OBJ_HANDLE_SIZE
484 	 * obj_handle_num can be from DT node "baseboard" or sysinfo driver.
485 	 */
486 	t = map_sysmem(*current, len);
487 	memset(t, 0, len);
488 	fill_smbios_header(t, SMBIOS_BOARD_INFORMATION, len, handle);
489 
490 	/* eos is at the end of the structure */
491 	eos_addr = (u8 *)t + len - sizeof(t->eos);
492 	smbios_set_eos(ctx, eos_addr);
493 
494 	t->manufacturer = smbios_add_prop_si(ctx, "manufacturer",
495 					     SYSID_SM_BASEBOARD_MANUFACTURER,
496 					     NULL);
497 	t->product_name = smbios_add_prop_si(ctx, "product",
498 					     SYSID_SM_BASEBOARD_PRODUCT, NULL);
499 	t->version = smbios_add_prop_si(ctx, "version",
500 					SYSID_SM_BASEBOARD_VERSION, NULL);
501 	t->serial_number = smbios_add_prop_si(ctx, "serial",
502 					      SYSID_SM_BASEBOARD_SERIAL, NULL);
503 	t->asset_tag_number = smbios_add_prop_si(ctx, "asset-tag",
504 						 SYSID_SM_BASEBOARD_ASSET_TAG,
505 						 NULL);
506 	t->feature_flags = smbios_get_val_si(ctx, "feature-flags",
507 					     SYSID_SM_BASEBOARD_FEATURE, 0);
508 
509 	t->chassis_location =
510 		smbios_add_prop_si(ctx, "chassis-location",
511 				   SYSID_SM_BASEBOARD_CHASSIS_LOCAT, NULL);
512 	t->board_type =	smbios_get_val_si(ctx, "board-type",
513 					  SYSID_SM_BASEBOARD_TYPE,
514 					  SMBIOS_BOARD_TYPE_UNKNOWN);
515 
516 	/*
517 	 * TODO:
518 	 * Populate the Contained Object Handles if they exist
519 	 * t->number_contained_objects = <obj_handle_num>;
520 	 */
521 
522 	t->chassis_handle = handle + 1;
523 
524 	len = t->hdr.length + smbios_string_table_len(ctx);
525 	*current += len;
526 	unmap_sysmem(t);
527 
528 	return len;
529 }
530 
smbios_write_type3(ulong * current,int handle,struct smbios_ctx * ctx)531 static int smbios_write_type3(ulong *current, int handle,
532 			      struct smbios_ctx *ctx)
533 {
534 	struct smbios_type3 *t;
535 	int len = sizeof(*t);
536 	u8 *eos_addr;
537 	size_t elem_size = 0;
538 	__maybe_unused u8 *elem_addr;
539 	__maybe_unused u8 *sku_num_addr;
540 
541 	/*
542 	 * reserve the space for the dynamic bytes of contained elements.
543 	 * TODO: elem_size = <element_count> * <element_record_length>
544 	 * element_count and element_record_length can be from DT node
545 	 * "chassis" or sysinfo driver.
546 	 */
547 	len += elem_size;
548 
549 	t = map_sysmem(*current, len);
550 	memset(t, 0, len);
551 	fill_smbios_header(t, SMBIOS_SYSTEM_ENCLOSURE, len, handle);
552 #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE)
553 	elem_addr = (u8 *)t + offsetof(struct smbios_type3, sku_number);
554 	sku_num_addr = elem_addr + elem_size;
555 #endif
556 	/* eos is at the end of the structure */
557 	eos_addr = (u8 *)t + len - sizeof(t->eos);
558 	smbios_set_eos(ctx, eos_addr);
559 
560 	t->manufacturer = smbios_add_prop_si(ctx, "manufacturer",
561 					     SYSID_SM_ENCLOSURE_MANUFACTURER,
562 					     NULL);
563 	t->chassis_type = smbios_get_val_si(ctx, "chassis-type",
564 					    SYSID_SM_ENCLOSURE_TYPE,
565 					    SMBIOS_ENCLOSURE_UNKNOWN);
566 	t->bootup_state = smbios_get_val_si(ctx, "bootup-state",
567 					    SYSID_SM_ENCLOSURE_BOOTUP,
568 					    SMBIOS_STATE_UNKNOWN);
569 	t->power_supply_state = smbios_get_val_si(ctx, "power-supply-state",
570 						  SYSID_SM_ENCLOSURE_POW,
571 						  SMBIOS_STATE_UNKNOWN);
572 	t->thermal_state = smbios_get_val_si(ctx, "thermal-state",
573 					     SYSID_SM_ENCLOSURE_THERMAL,
574 					     SMBIOS_STATE_UNKNOWN);
575 	t->security_status = smbios_get_val_si(ctx, "security-status",
576 					       SYSID_SM_ENCLOSURE_SECURITY,
577 					       SMBIOS_SECURITY_UNKNOWN);
578 
579 #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE)
580 	t->version = smbios_add_prop_si(ctx, "version",
581 					SYSID_SM_ENCLOSURE_VERSION, NULL);
582 	t->serial_number = smbios_add_prop_si(ctx, "serial",
583 					      SYSID_SM_ENCLOSURE_SERIAL, NULL);
584 	t->asset_tag_number = smbios_add_prop_si(ctx, "asset-tag",
585 						 SYSID_SM_BASEBOARD_ASSET_TAG,
586 						 NULL);
587 	t->oem_defined = smbios_get_val_si(ctx, "oem-defined",
588 					   SYSID_SM_ENCLOSURE_OEM, 0);
589 	t->height = smbios_get_val_si(ctx, "height",
590 				      SYSID_SM_ENCLOSURE_HEIGHT, 0);
591 	t->number_of_power_cords =
592 		smbios_get_val_si(ctx, "number-of-power-cords",
593 				  SYSID_SM_ENCLOSURE_POWCORE_NUM, 0);
594 
595 	/*
596 	 * TODO: Populate the Contained Element Record if they exist
597 	 * t->element_count = <element_num>;
598 	 * t->element_record_length = <element_len>;
599 	 */
600 
601 	*sku_num_addr =	smbios_add_prop_si(ctx, "sku", SYSID_SM_ENCLOSURE_SKU,
602 					   NULL);
603 #endif
604 
605 	len = t->hdr.length + smbios_string_table_len(ctx);
606 	*current += len;
607 	unmap_sysmem(t);
608 
609 	return len;
610 }
611 
smbios_write_type4_dm(struct smbios_type4 * t,struct smbios_ctx * ctx)612 static void smbios_write_type4_dm(struct smbios_type4 *t,
613 				  struct smbios_ctx *ctx)
614 {
615 	u16 processor_family = SMBIOS_PROCESSOR_FAMILY_UNKNOWN;
616 	const char *vendor = NULL;
617 	const char *name = NULL;
618 	__maybe_unused void *id_data = NULL;
619 	__maybe_unused size_t id_size = 0;
620 
621 #ifdef CONFIG_CPU
622 	char processor_name[49];
623 	char vendor_name[49];
624 	struct udevice *cpu = NULL;
625 
626 	uclass_find_first_device(UCLASS_CPU, &cpu);
627 	if (cpu) {
628 		struct cpu_plat *plat = dev_get_parent_plat(cpu);
629 
630 		if (plat->family)
631 			processor_family = plat->family;
632 		t->processor_id[0] = plat->id[0];
633 		t->processor_id[1] = plat->id[1];
634 
635 		if (!cpu_get_vendor(cpu, vendor_name, sizeof(vendor_name)))
636 			vendor = vendor_name;
637 		if (!cpu_get_desc(cpu, processor_name, sizeof(processor_name)))
638 			name = processor_name;
639 	}
640 #endif
641 	if (processor_family == SMBIOS_PROCESSOR_FAMILY_UNKNOWN)
642 		processor_family =
643 			smbios_get_val_si(ctx, "family",
644 					  SYSID_SM_PROCESSOR_FAMILY,
645 					  SMBIOS_PROCESSOR_FAMILY_UNKNOWN);
646 
647 	if (processor_family == SMBIOS_PROCESSOR_FAMILY_EXT)
648 		t->processor_family2 =
649 			smbios_get_val_si(ctx, "family2",
650 					  SYSID_SM_PROCESSOR_FAMILY2,
651 					  SMBIOS_PROCESSOR_FAMILY_UNKNOWN);
652 
653 	t->processor_family = processor_family;
654 	t->processor_manufacturer =
655 		smbios_add_prop_si(ctx, "manufacturer",
656 				   SYSID_SM_PROCESSOR_MANUFACT, vendor);
657 	t->processor_version = smbios_add_prop_si(ctx, "version",
658 						  SYSID_SM_PROCESSOR_VERSION,
659 						  name);
660 
661 #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE)
662 	if (t->processor_id[0] || t->processor_id[1] ||
663 	    sysinfo_get_data(ctx->dev, SYSID_SM_PROCESSOR_ID, &id_data,
664 			     &id_size))
665 		return;
666 
667 	if (id_data && id_size == sizeof(t->processor_id))
668 		memcpy((u8 *)t->processor_id, id_data, id_size);
669 #endif
670 }
671 
smbios_write_type4(ulong * current,int handle,struct smbios_ctx * ctx)672 static int smbios_write_type4(ulong *current, int handle,
673 			      struct smbios_ctx *ctx)
674 {
675 	struct smbios_type4 *t;
676 	int len = sizeof(*t);
677 	__maybe_unused void *hdl;
678 	__maybe_unused size_t hdl_size;
679 
680 	t = map_sysmem(*current, len);
681 	memset(t, 0, len);
682 	fill_smbios_header(t, SMBIOS_PROCESSOR_INFORMATION, len, handle);
683 	smbios_set_eos(ctx, t->eos);
684 	t->socket_design = smbios_add_prop_si(ctx, "socket-design",
685 					      SYSID_SM_PROCESSOR_SOCKET, NULL);
686 	t->processor_type = smbios_get_val_si(ctx, "processor-type",
687 					      SYSID_SM_PROCESSOR_TYPE,
688 					      SMBIOS_PROCESSOR_TYPE_UNKNOWN);
689 	smbios_write_type4_dm(t, ctx);
690 
691 	t->status = smbios_get_val_si(ctx, "processor-status",
692 				      SYSID_SM_PROCESSOR_STATUS,
693 				      SMBIOS_PROCESSOR_STATUS_UNKNOWN);
694 	t->processor_upgrade =
695 		smbios_get_val_si(ctx, "upgrade", SYSID_SM_PROCESSOR_UPGRADE,
696 				  SMBIOS_PROCESSOR_UPGRADE_UNKNOWN);
697 
698 	t->l1_cache_handle = SMBIOS_CACHE_HANDLE_NONE;
699 	t->l2_cache_handle = SMBIOS_CACHE_HANDLE_NONE;
700 	t->l3_cache_handle = SMBIOS_CACHE_HANDLE_NONE;
701 
702 #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE)
703 	t->voltage = smbios_get_val_si(ctx, "voltage",
704 				       SYSID_SM_PROCESSOR_VOLTAGE, 0);
705 	t->external_clock = smbios_get_val_si(ctx, "external-clock",
706 					      SYSID_SM_PROCESSOR_EXT_CLOCK, 0);
707 	t->max_speed = smbios_get_val_si(ctx, "max-speed",
708 					 SYSID_SM_PROCESSOR_MAX_SPEED, 0);
709 	t->current_speed = smbios_get_val_si(ctx, "current-speed",
710 					     SYSID_SM_PROCESSOR_CUR_SPEED, 0);
711 
712 	/* Read the cache handles */
713 	if (!sysinfo_get_data(ctx->dev, SYSID_SM_CACHE_HANDLE, &hdl,
714 			      &hdl_size) &&
715 	    (hdl_size == SYSINFO_CACHE_LVL_MAX * sizeof(u16))) {
716 		u16 *handle = (u16 *)hdl;
717 
718 		if (*handle)
719 			t->l1_cache_handle = *handle;
720 
721 		handle++;
722 		if (*handle)
723 			t->l2_cache_handle = *handle;
724 
725 		handle++;
726 		if (*handle)
727 			t->l3_cache_handle = *handle;
728 	}
729 
730 	t->serial_number = smbios_add_prop_si(ctx, "serial",
731 					      SYSID_SM_PROCESSOR_SN, NULL);
732 	t->asset_tag = smbios_add_prop_si(ctx, "asset-tag",
733 					  SYSID_SM_PROCESSOR_ASSET_TAG, NULL);
734 	t->part_number = smbios_add_prop_si(ctx, "part-number",
735 					    SYSID_SM_PROCESSOR_PN, NULL);
736 	t->core_count =	smbios_get_val_si(ctx, "core-count",
737 					  SYSID_SM_PROCESSOR_CORE_CNT, 0);
738 	t->core_enabled = smbios_get_val_si(ctx, "core-enabled",
739 					    SYSID_SM_PROCESSOR_CORE_EN, 0);
740 	t->thread_count = smbios_get_val_si(ctx, "thread-count",
741 					    SYSID_SM_PROCESSOR_THREAD_CNT, 0);
742 	t->processor_characteristics =
743 		smbios_get_val_si(ctx, "characteristics",
744 				  SYSID_SM_PROCESSOR_CHARA,
745 				  SMBIOS_PROCESSOR_UND);
746 	t->core_count2 = smbios_get_val_si(ctx, "core-count2",
747 					   SYSID_SM_PROCESSOR_CORE_CNT2, 0);
748 	t->core_enabled2 = smbios_get_val_si(ctx, "core-enabled2",
749 					     SYSID_SM_PROCESSOR_CORE_EN2, 0);
750 	t->thread_count2 = smbios_get_val_si(ctx, "thread-count2",
751 					     SYSID_SM_PROCESSOR_THREAD_CNT2, 0);
752 	t->thread_enabled = smbios_get_val_si(ctx, "thread-enabled",
753 					      SYSID_SM_PROCESSOR_THREAD_EN, 0);
754 #endif
755 
756 	len = t->hdr.length + smbios_string_table_len(ctx);
757 	*current += len;
758 	unmap_sysmem(t);
759 
760 	return len;
761 }
762 
763 #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE)
764 
smbios_write_type7_1level(ulong * current,int handle,struct smbios_ctx * ctx,int level)765 static int smbios_write_type7_1level(ulong *current, int handle,
766 				     struct smbios_ctx *ctx, int level)
767 {
768 	struct smbios_type7 *t;
769 	int len = sizeof(*t);
770 	void *hdl;
771 	size_t hdl_size;
772 
773 	t = map_sysmem(*current, len);
774 	memset(t, 0, len);
775 	fill_smbios_header(t, SMBIOS_CACHE_INFORMATION, len, handle);
776 	smbios_set_eos(ctx, t->eos);
777 
778 	t->socket_design = smbios_add_prop_si(ctx, "socket-design",
779 					      SYSID_SM_CACHE_SOCKET + level,
780 					      NULL);
781 	t->config.data = smbios_get_val_si(ctx, "config",
782 					   SYSID_SM_CACHE_CONFIG + level,
783 					   (level - 1) | SMBIOS_CACHE_OP_UND);
784 	t->max_size.data = smbios_get_val_si(ctx, "max-size",
785 					     SYSID_SM_CACHE_MAX_SIZE + level,
786 					     0);
787 	t->inst_size.data = smbios_get_val_si(ctx, "installed-size",
788 					      SYSID_SM_CACHE_INST_SIZE + level,
789 					      0);
790 	t->supp_sram_type.data =
791 		smbios_get_val_si(ctx, "supported-sram-type",
792 				  SYSID_SM_CACHE_SUPSRAM_TYPE + level,
793 				  SMBIOS_CACHE_SRAM_TYPE_UNKNOWN);
794 	t->curr_sram_type.data =
795 		smbios_get_val_si(ctx, "current-sram-type",
796 				  SYSID_SM_CACHE_CURSRAM_TYPE + level,
797 				  SMBIOS_CACHE_SRAM_TYPE_UNKNOWN);
798 	t->speed = smbios_get_val_si(ctx, "speed", SYSID_SM_CACHE_SPEED + level,
799 				     0);
800 	t->err_corr_type = smbios_get_val_si(ctx, "error-correction-type",
801 					     SYSID_SM_CACHE_ERRCOR_TYPE + level,
802 					     SMBIOS_CACHE_ERRCORR_UNKNOWN);
803 	t->sys_cache_type =
804 		smbios_get_val_si(ctx, "system-cache-type",
805 				  SYSID_SM_CACHE_SCACHE_TYPE + level,
806 				  SMBIOS_CACHE_SYSCACHE_TYPE_UNKNOWN);
807 	t->associativity = smbios_get_val_si(ctx, "associativity",
808 					     SYSID_SM_CACHE_ASSOC + level,
809 					     SMBIOS_CACHE_ASSOC_UNKNOWN);
810 	t->max_size2.data = smbios_get_val_si(ctx, "max-size2",
811 					      SYSID_SM_CACHE_MAX_SIZE2 + level,
812 					      0);
813 	t->inst_size2.data =
814 		smbios_get_val_si(ctx, "installed-size2",
815 				  SYSID_SM_CACHE_INST_SIZE2 + level, 0);
816 
817 	/* Save the cache handles */
818 	if (!sysinfo_get_data(ctx->dev, SYSID_SM_CACHE_HANDLE, &hdl,
819 			      &hdl_size)) {
820 		if (hdl_size == SYSINFO_CACHE_LVL_MAX * sizeof(u16))
821 			*((u16 *)hdl + level) = handle;
822 	}
823 
824 	len = t->hdr.length + smbios_string_table_len(ctx);
825 	*current += len;
826 	unmap_sysmem(t);
827 
828 	return len;
829 }
830 
smbios_write_type7(ulong * current,int handle,struct smbios_ctx * ctx)831 static int smbios_write_type7(ulong *current, int handle,
832 			      struct smbios_ctx *ctx)
833 {
834 	int len = 0;
835 	int i, level;
836 	ofnode parent = ctx->node;
837 	struct smbios_ctx ctx_bak;
838 
839 	memcpy(&ctx_bak, ctx, sizeof(ctx_bak));
840 
841 	/* Get the number of level */
842 	level =	smbios_get_val_si(ctx, NULL, SYSID_SM_CACHE_LEVEL, 0);
843 	if (level >= SYSINFO_CACHE_LVL_MAX) /* Error, return 0-length */
844 		return 0;
845 
846 	for (i = 0; i <= level; i++) {
847 		char buf[9] = "";
848 
849 		if (!snprintf(buf, sizeof(buf), "l%d-cache", i + 1))
850 			return 0;
851 		ctx->subnode_name = buf;
852 		ctx->node = ofnode_find_subnode(parent, ctx->subnode_name);
853 		len += smbios_write_type7_1level(current, handle++, ctx, i);
854 		memcpy(ctx, &ctx_bak, sizeof(*ctx));
855 	}
856 	return len;
857 }
858 
859 #endif /* #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE) */
860 
smbios_write_type32(ulong * current,int handle,struct smbios_ctx * ctx)861 static int smbios_write_type32(ulong *current, int handle,
862 			       struct smbios_ctx *ctx)
863 {
864 	struct smbios_type32 *t;
865 	int len = sizeof(*t);
866 
867 	t = map_sysmem(*current, len);
868 	memset(t, 0, len);
869 	fill_smbios_header(t, SMBIOS_SYSTEM_BOOT_INFORMATION, len, handle);
870 	smbios_set_eos(ctx, t->eos);
871 
872 	*current += len;
873 	unmap_sysmem(t);
874 
875 	return len;
876 }
877 
smbios_write_type127(ulong * current,int handle,struct smbios_ctx * ctx)878 static int smbios_write_type127(ulong *current, int handle,
879 				struct smbios_ctx *ctx)
880 {
881 	struct smbios_type127 *t;
882 	int len = sizeof(*t);
883 
884 	t = map_sysmem(*current, len);
885 	memset(t, 0, len);
886 	fill_smbios_header(t, SMBIOS_END_OF_TABLE, len, handle);
887 
888 	*current += len;
889 	unmap_sysmem(t);
890 
891 	return len;
892 }
893 
894 static struct smbios_write_method smbios_write_funcs[] = {
895 	{ smbios_write_type0, "bios", },
896 	{ smbios_write_type1, "system", },
897 	{ smbios_write_type2, "baseboard", },
898 	/* Type 3 must immediately follow type 2 due to chassis handle. */
899 	{ smbios_write_type3, "chassis", },
900 #if IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE_VERBOSE)
901 	/* Type 7 must ahead of type 4 to get cache handles. */
902 	{ smbios_write_type7, "cache", },
903 #endif
904 	{ smbios_write_type4, "processor"},
905 	{ smbios_write_type32, },
906 	{ smbios_write_type127 },
907 };
908 
write_smbios_table(ulong addr)909 ulong write_smbios_table(ulong addr)
910 {
911 	ofnode parent_node = ofnode_null();
912 	ulong table_addr, start_addr;
913 	struct smbios3_entry *se;
914 	struct smbios_ctx ctx;
915 	ulong tables;
916 	int len = 0;
917 	int handle = 0;
918 	int i;
919 
920 	ctx.node = ofnode_null();
921 	if (CONFIG_IS_ENABLED(SYSINFO)) {
922 		uclass_first_device(UCLASS_SYSINFO, &ctx.dev);
923 		if (ctx.dev) {
924 			int ret;
925 
926 			parent_node = dev_read_subnode(ctx.dev, "smbios");
927 			ret = sysinfo_detect(ctx.dev);
928 
929 			/*
930 			 * ignore the error since many boards don't implement
931 			 * this and we can still use the info in the devicetree
932 			 */
933 			ret = log_msg_ret("sys", ret);
934 		}
935 	} else {
936 		ctx.dev = NULL;
937 	}
938 
939 	start_addr = addr;
940 
941 	/* move past the (so-far-unwritten) header to start writing structs */
942 	addr = ALIGN(addr + sizeof(struct smbios3_entry), 16);
943 	tables = addr;
944 
945 	/* populate minimum required tables */
946 	for (i = 0; i < ARRAY_SIZE(smbios_write_funcs); i++) {
947 		const struct smbios_write_method *method;
948 
949 		method = &smbios_write_funcs[i];
950 		ctx.subnode_name = NULL;
951 		if (method->subnode_name) {
952 			ctx.subnode_name = method->subnode_name;
953 			if (ofnode_valid(parent_node))
954 				ctx.node = ofnode_find_subnode(parent_node,
955 							       method->subnode_name);
956 		}
957 		len += method->write((ulong *)&addr, handle++, &ctx);
958 	}
959 
960 	/*
961 	 * We must use a pointer here so things work correctly on sandbox. The
962 	 * user of this table is not aware of the mapping of addresses to
963 	 * sandbox's DRAM buffer.
964 	 */
965 	table_addr = (ulong)map_sysmem(tables, 0);
966 
967 	/* now go back and write the SMBIOS3 header */
968 	se = map_sysmem(start_addr, sizeof(struct smbios3_entry));
969 	memset(se, '\0', sizeof(struct smbios3_entry));
970 	memcpy(se->anchor, "_SM3_", 5);
971 	se->length = sizeof(struct smbios3_entry);
972 	se->major_ver = SMBIOS_MAJOR_VER;
973 	se->minor_ver = SMBIOS_MINOR_VER;
974 	se->doc_rev = 0;
975 	se->entry_point_rev = 1;
976 	se->table_maximum_size = len;
977 	se->struct_table_address = table_addr;
978 	se->checksum = table_compute_checksum(se, sizeof(struct smbios3_entry));
979 	unmap_sysmem(se);
980 
981 	return addr;
982 }
983