xref: /openbmc/u-boot/lib/smbios.c (revision 14453fbfadc2f98ca35d6033140466c7a4b4947a)
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  #include <common.h>
9  #include <mapmem.h>
10  #include <smbios.h>
11  #include <tables_csum.h>
12  #include <version.h>
13  #ifdef CONFIG_CPU
14  #include <cpu.h>
15  #include <dm.h>
16  #include <dm/uclass-internal.h>
17  #endif
18  
19  /**
20   * smbios_add_string() - add a string to the string area
21   *
22   * This adds a string to the string area which is appended directly after
23   * the formatted portion of an SMBIOS structure.
24   *
25   * @start:	string area start address
26   * @str:	string to add
27   * @return:	string number in the string area
28   */
29  static int smbios_add_string(char *start, const char *str)
30  {
31  	int i = 1;
32  	char *p = start;
33  
34  	for (;;) {
35  		if (!*p) {
36  			strcpy(p, str);
37  			p += strlen(str);
38  			*p++ = '\0';
39  			*p++ = '\0';
40  
41  			return i;
42  		}
43  
44  		if (!strcmp(p, str))
45  			return i;
46  
47  		p += strlen(p) + 1;
48  		i++;
49  	}
50  }
51  
52  /**
53   * smbios_string_table_len() - compute the string area size
54   *
55   * This computes the size of the string area including the string terminator.
56   *
57   * @start:	string area start address
58   * @return:	string area size
59   */
60  static int smbios_string_table_len(char *start)
61  {
62  	char *p = start;
63  	int i, len = 0;
64  
65  	while (*p) {
66  		i = strlen(p) + 1;
67  		p += i;
68  		len += i;
69  	}
70  
71  	return len + 1;
72  }
73  
74  static int smbios_write_type0(ulong *current, int handle)
75  {
76  	struct smbios_type0 *t;
77  	int len = sizeof(struct smbios_type0);
78  
79  	t = map_sysmem(*current, len);
80  	memset(t, 0, sizeof(struct smbios_type0));
81  	fill_smbios_header(t, SMBIOS_BIOS_INFORMATION, len, handle);
82  	t->vendor = smbios_add_string(t->eos, "U-Boot");
83  	t->bios_ver = smbios_add_string(t->eos, PLAIN_VERSION);
84  	t->bios_release_date = smbios_add_string(t->eos, U_BOOT_DMI_DATE);
85  #ifdef CONFIG_ROM_SIZE
86  	t->bios_rom_size = (CONFIG_ROM_SIZE / 65536) - 1;
87  #endif
88  	t->bios_characteristics = BIOS_CHARACTERISTICS_PCI_SUPPORTED |
89  				  BIOS_CHARACTERISTICS_SELECTABLE_BOOT |
90  				  BIOS_CHARACTERISTICS_UPGRADEABLE;
91  #ifdef CONFIG_GENERATE_ACPI_TABLE
92  	t->bios_characteristics_ext1 = BIOS_CHARACTERISTICS_EXT1_ACPI;
93  #endif
94  #ifdef CONFIG_EFI_LOADER
95  	t->bios_characteristics_ext1 |= BIOS_CHARACTERISTICS_EXT1_UEFI;
96  #endif
97  	t->bios_characteristics_ext2 = BIOS_CHARACTERISTICS_EXT2_TARGET;
98  
99  	t->bios_major_release = 0xff;
100  	t->bios_minor_release = 0xff;
101  	t->ec_major_release = 0xff;
102  	t->ec_minor_release = 0xff;
103  
104  	len = t->length + smbios_string_table_len(t->eos);
105  	*current += len;
106  	unmap_sysmem(t);
107  
108  	return len;
109  }
110  
111  static int smbios_write_type1(ulong *current, int handle)
112  {
113  	struct smbios_type1 *t;
114  	int len = sizeof(struct smbios_type1);
115  	char *serial_str = env_get("serial#");
116  
117  	t = map_sysmem(*current, len);
118  	memset(t, 0, sizeof(struct smbios_type1));
119  	fill_smbios_header(t, SMBIOS_SYSTEM_INFORMATION, len, handle);
120  	t->manufacturer = smbios_add_string(t->eos, CONFIG_SMBIOS_MANUFACTURER);
121  	t->product_name = smbios_add_string(t->eos, CONFIG_SMBIOS_PRODUCT_NAME);
122  	if (serial_str) {
123  		strncpy((char *)t->uuid, serial_str, sizeof(t->uuid));
124  		t->serial_number = smbios_add_string(t->eos, serial_str);
125  	}
126  
127  	len = t->length + smbios_string_table_len(t->eos);
128  	*current += len;
129  	unmap_sysmem(t);
130  
131  	return len;
132  }
133  
134  static int smbios_write_type2(ulong *current, int handle)
135  {
136  	struct smbios_type2 *t;
137  	int len = sizeof(struct smbios_type2);
138  
139  	t = map_sysmem(*current, len);
140  	memset(t, 0, sizeof(struct smbios_type2));
141  	fill_smbios_header(t, SMBIOS_BOARD_INFORMATION, len, handle);
142  	t->manufacturer = smbios_add_string(t->eos, CONFIG_SMBIOS_MANUFACTURER);
143  	t->product_name = smbios_add_string(t->eos, CONFIG_SMBIOS_PRODUCT_NAME);
144  	t->feature_flags = SMBIOS_BOARD_FEATURE_HOSTING;
145  	t->board_type = SMBIOS_BOARD_MOTHERBOARD;
146  
147  	len = t->length + smbios_string_table_len(t->eos);
148  	*current += len;
149  	unmap_sysmem(t);
150  
151  	return len;
152  }
153  
154  static int smbios_write_type3(ulong *current, int handle)
155  {
156  	struct smbios_type3 *t;
157  	int len = sizeof(struct smbios_type3);
158  
159  	t = map_sysmem(*current, len);
160  	memset(t, 0, sizeof(struct smbios_type3));
161  	fill_smbios_header(t, SMBIOS_SYSTEM_ENCLOSURE, len, handle);
162  	t->manufacturer = smbios_add_string(t->eos, CONFIG_SMBIOS_MANUFACTURER);
163  	t->chassis_type = SMBIOS_ENCLOSURE_DESKTOP;
164  	t->bootup_state = SMBIOS_STATE_SAFE;
165  	t->power_supply_state = SMBIOS_STATE_SAFE;
166  	t->thermal_state = SMBIOS_STATE_SAFE;
167  	t->security_status = SMBIOS_SECURITY_NONE;
168  
169  	len = t->length + smbios_string_table_len(t->eos);
170  	*current += len;
171  	unmap_sysmem(t);
172  
173  	return len;
174  }
175  
176  static void smbios_write_type4_dm(struct smbios_type4 *t)
177  {
178  	u16 processor_family = SMBIOS_PROCESSOR_FAMILY_UNKNOWN;
179  	const char *vendor = "Unknown";
180  	const char *name = "Unknown";
181  
182  #ifdef CONFIG_CPU
183  	char processor_name[49];
184  	char vendor_name[49];
185  	struct udevice *dev = NULL;
186  
187  	uclass_find_first_device(UCLASS_CPU, &dev);
188  	if (dev) {
189  		struct cpu_platdata *plat = dev_get_parent_platdata(dev);
190  
191  		if (plat->family)
192  			processor_family = plat->family;
193  		t->processor_id[0] = plat->id[0];
194  		t->processor_id[1] = plat->id[1];
195  
196  		if (!cpu_get_vendor(dev, vendor_name, sizeof(vendor_name)))
197  			vendor = vendor_name;
198  		if (!cpu_get_desc(dev, processor_name, sizeof(processor_name)))
199  			name = processor_name;
200  	}
201  #endif
202  
203  	t->processor_family = processor_family;
204  	t->processor_manufacturer = smbios_add_string(t->eos, vendor);
205  	t->processor_version = smbios_add_string(t->eos, name);
206  }
207  
208  static int smbios_write_type4(ulong *current, int handle)
209  {
210  	struct smbios_type4 *t;
211  	int len = sizeof(struct smbios_type4);
212  
213  	t = map_sysmem(*current, len);
214  	memset(t, 0, sizeof(struct smbios_type4));
215  	fill_smbios_header(t, SMBIOS_PROCESSOR_INFORMATION, len, handle);
216  	t->processor_type = SMBIOS_PROCESSOR_TYPE_CENTRAL;
217  	smbios_write_type4_dm(t);
218  	t->status = SMBIOS_PROCESSOR_STATUS_ENABLED;
219  	t->processor_upgrade = SMBIOS_PROCESSOR_UPGRADE_NONE;
220  	t->l1_cache_handle = 0xffff;
221  	t->l2_cache_handle = 0xffff;
222  	t->l3_cache_handle = 0xffff;
223  	t->processor_family2 = t->processor_family;
224  
225  	len = t->length + smbios_string_table_len(t->eos);
226  	*current += len;
227  	unmap_sysmem(t);
228  
229  	return len;
230  }
231  
232  static int smbios_write_type32(ulong *current, int handle)
233  {
234  	struct smbios_type32 *t;
235  	int len = sizeof(struct smbios_type32);
236  
237  	t = map_sysmem(*current, len);
238  	memset(t, 0, sizeof(struct smbios_type32));
239  	fill_smbios_header(t, SMBIOS_SYSTEM_BOOT_INFORMATION, len, handle);
240  
241  	*current += len;
242  	unmap_sysmem(t);
243  
244  	return len;
245  }
246  
247  static int smbios_write_type127(ulong *current, int handle)
248  {
249  	struct smbios_type127 *t;
250  	int len = sizeof(struct smbios_type127);
251  
252  	t = map_sysmem(*current, len);
253  	memset(t, 0, sizeof(struct smbios_type127));
254  	fill_smbios_header(t, SMBIOS_END_OF_TABLE, len, handle);
255  
256  	*current += len;
257  	unmap_sysmem(t);
258  
259  	return len;
260  }
261  
262  static smbios_write_type smbios_write_funcs[] = {
263  	smbios_write_type0,
264  	smbios_write_type1,
265  	smbios_write_type2,
266  	smbios_write_type3,
267  	smbios_write_type4,
268  	smbios_write_type32,
269  	smbios_write_type127
270  };
271  
272  ulong write_smbios_table(ulong addr)
273  {
274  	struct smbios_entry *se;
275  	ulong table_addr;
276  	ulong tables;
277  	int len = 0;
278  	int max_struct_size = 0;
279  	int handle = 0;
280  	char *istart;
281  	int isize;
282  	int i;
283  
284  	/* 16 byte align the table address */
285  	addr = ALIGN(addr, 16);
286  
287  	se = map_sysmem(addr, sizeof(struct smbios_entry));
288  	memset(se, 0, sizeof(struct smbios_entry));
289  
290  	addr += sizeof(struct smbios_entry);
291  	addr = ALIGN(addr, 16);
292  	tables = addr;
293  
294  	/* populate minimum required tables */
295  	for (i = 0; i < ARRAY_SIZE(smbios_write_funcs); i++) {
296  		int tmp = smbios_write_funcs[i]((ulong *)&addr, handle++);
297  
298  		max_struct_size = max(max_struct_size, tmp);
299  		len += tmp;
300  	}
301  
302  	memcpy(se->anchor, "_SM_", 4);
303  	se->length = sizeof(struct smbios_entry);
304  	se->major_ver = SMBIOS_MAJOR_VER;
305  	se->minor_ver = SMBIOS_MINOR_VER;
306  	se->max_struct_size = max_struct_size;
307  	memcpy(se->intermediate_anchor, "_DMI_", 5);
308  	se->struct_table_length = len;
309  
310  	/*
311  	 * We must use a pointer here so things work correctly on sandbox. The
312  	 * user of this table is not aware of the mapping of addresses to
313  	 * sandbox's DRAM buffer.
314  	 */
315  	table_addr = (ulong)map_sysmem(tables, 0);
316  	if (sizeof(table_addr) > sizeof(u32) && table_addr > (ulong)UINT_MAX) {
317  		/*
318  		 * We need to put this >32-bit pointer into the table but the
319  		 * field is only 32 bits wide.
320  		 */
321  		printf("WARNING: SMBIOS table_address overflow %llx\n",
322  		       (unsigned long long)table_addr);
323  		table_addr = 0;
324  	}
325  	se->struct_table_address = table_addr;
326  
327  	se->struct_count = handle;
328  
329  	/* calculate checksums */
330  	istart = (char *)se + SMBIOS_INTERMEDIATE_OFFSET;
331  	isize = sizeof(struct smbios_entry) - SMBIOS_INTERMEDIATE_OFFSET;
332  	se->intermediate_checksum = table_compute_checksum(istart, isize);
333  	se->checksum = table_compute_checksum(se, sizeof(struct smbios_entry));
334  	unmap_sysmem(se);
335  
336  	return addr;
337  }
338