smbios: Allow writing to the coreboot version string
[platform/kernel/u-boot.git] / lib / smbios.c
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 <dm.h>
10 #include <env.h>
11 #include <mapmem.h>
12 #include <smbios.h>
13 #include <sysinfo.h>
14 #include <tables_csum.h>
15 #include <version.h>
16 #ifdef CONFIG_CPU
17 #include <cpu.h>
18 #include <dm/uclass-internal.h>
19 #endif
20
21 DECLARE_GLOBAL_DATA_PTR;
22
23 /**
24  * struct smbios_ctx - context for writing SMBIOS tables
25  *
26  * @node:       node containing the information to write (ofnode_null() if none)
27  * @dev:        sysinfo device to use (NULL if none)
28  * @eos:        end-of-string pointer for the table being processed. This is set
29  *              up when we start processing a table
30  * @next_ptr:   pointer to the start of the next string to be added. When the
31  *              table is nopt empty, this points to the byte after the \0 of the
32  *              previous string.
33  * @last_str:   points to the last string that was written to the table, or NULL
34  *              if none
35  */
36 struct smbios_ctx {
37         ofnode node;
38         struct udevice *dev;
39         char *eos;
40         char *next_ptr;
41         char *last_str;
42 };
43
44 /**
45  * Function prototype to write a specific type of SMBIOS structure
46  *
47  * @addr:       start address to write the structure
48  * @handle:     the structure's handle, a unique 16-bit number
49  * @ctx:        context for writing the tables
50  * @return:     size of the structure
51  */
52 typedef int (*smbios_write_type)(ulong *addr, int handle,
53                                  struct smbios_ctx *ctx);
54
55 /**
56  * struct smbios_write_method - Information about a table-writing function
57  *
58  * @write: Function to call
59  * @subnode_name: Name of subnode which has the information for this function,
60  *      NULL if none
61  */
62 struct smbios_write_method {
63         smbios_write_type write;
64         const char *subnode_name;
65 };
66
67 /**
68  * smbios_add_string() - add a string to the string area
69  *
70  * This adds a string to the string area which is appended directly after
71  * the formatted portion of an SMBIOS structure.
72  *
73  * @ctx:        SMBIOS context
74  * @str:        string to add
75  * @return:     string number in the string area (1 or more)
76  */
77 static int smbios_add_string(struct smbios_ctx *ctx, const char *str)
78 {
79         int i = 1;
80         char *p = ctx->eos;
81
82         if (!*str)
83                 str = "Unknown";
84
85         for (;;) {
86                 if (!*p) {
87                         ctx->last_str = p;
88                         strcpy(p, str);
89                         p += strlen(str);
90                         *p++ = '\0';
91                         ctx->next_ptr = p;
92                         *p++ = '\0';
93
94                         return i;
95                 }
96
97                 if (!strcmp(p, str)) {
98                         ctx->last_str = p;
99                         return i;
100                 }
101
102                 p += strlen(p) + 1;
103                 i++;
104         }
105 }
106
107 /**
108  * smbios_add_prop_si() - Add a property from the devicetree or sysinfo
109  *
110  * Sysinfo is used if available, with a fallback to devicetree
111  *
112  * @ctx:        context for writing the tables
113  * @prop:       property to write
114  * @return 0 if not found, else SMBIOS string number (1 or more)
115  */
116 static int smbios_add_prop_si(struct smbios_ctx *ctx, const char *prop,
117                               int sysinfo_id)
118 {
119         if (sysinfo_id && ctx->dev) {
120                 char val[SMBIOS_STR_MAX];
121                 int ret;
122
123                 ret = sysinfo_get_str(ctx->dev, sysinfo_id, sizeof(val), val);
124                 if (!ret)
125                         return smbios_add_string(ctx, val);
126         }
127         if (IS_ENABLED(CONFIG_OF_CONTROL)) {
128                 const char *str;
129
130                 str = ofnode_read_string(ctx->node, prop);
131                 if (str)
132                         return smbios_add_string(ctx, str);
133         }
134
135         return 0;
136 }
137
138 /**
139  * smbios_add_prop() - Add a property from the devicetree
140  *
141  * @prop:       property to write
142  * @return 0 if not found, else SMBIOS string number (1 or more)
143  */
144 static int smbios_add_prop(struct smbios_ctx *ctx, const char *prop)
145 {
146         return smbios_add_prop_si(ctx, prop, SYSINFO_ID_NONE);
147 }
148
149 static void smbios_set_eos(struct smbios_ctx *ctx, char *eos)
150 {
151         ctx->eos = eos;
152         ctx->next_ptr = eos;
153         ctx->last_str = NULL;
154 }
155
156 int smbios_update_version(const char *version)
157 {
158         char *ptr = gd->smbios_version;
159         uint old_len, len;
160
161         if (!ptr)
162                 return log_ret(-ENOENT);
163
164         /*
165          * This string is supposed to have at least enough bytes and is
166          * padded with spaces. Update it, taking care not to move the
167          * \0 terminator, so that other strings in the string table
168          * are not disturbed. See smbios_add_string()
169          */
170         old_len = strnlen(ptr, SMBIOS_STR_MAX);
171         len = strnlen(version, SMBIOS_STR_MAX);
172         if (len > old_len)
173                 return log_ret(-ENOSPC);
174
175         log_debug("Replacing SMBIOS type 0 version string '%s'\n", ptr);
176         memcpy(ptr, version, len);
177 #ifdef LOG_DEBUG
178         print_buffer((ulong)ptr, ptr, 1, old_len + 1, 0);
179 #endif
180
181         return 0;
182 }
183
184 /**
185  * smbios_string_table_len() - compute the string area size
186  *
187  * This computes the size of the string area including the string terminator.
188  *
189  * @ctx:        SMBIOS context
190  * @return:     string area size
191  */
192 static int smbios_string_table_len(const struct smbios_ctx *ctx)
193 {
194         /* Allow for the final \0 after all strings */
195         return (ctx->next_ptr + 1) - ctx->eos;
196 }
197
198 static int smbios_write_type0(ulong *current, int handle,
199                               struct smbios_ctx *ctx)
200 {
201         struct smbios_type0 *t;
202         int len = sizeof(struct smbios_type0);
203
204         t = map_sysmem(*current, len);
205         memset(t, 0, sizeof(struct smbios_type0));
206         fill_smbios_header(t, SMBIOS_BIOS_INFORMATION, len, handle);
207         smbios_set_eos(ctx, t->eos);
208         t->vendor = smbios_add_string(ctx, "U-Boot");
209
210         t->bios_ver = smbios_add_prop(ctx, "version");
211         if (!t->bios_ver)
212                 t->bios_ver = smbios_add_string(ctx, PLAIN_VERSION);
213         if (t->bios_ver)
214                 gd->smbios_version = ctx->last_str;
215         log_debug("smbios_version = %p: '%s'\n", gd->smbios_version,
216                   gd->smbios_version);
217 #ifdef LOG_DEBUG
218         print_buffer((ulong)gd->smbios_version, gd->smbios_version,
219                      1, strlen(gd->smbios_version) + 1, 0);
220 #endif
221         t->bios_release_date = smbios_add_string(ctx, U_BOOT_DMI_DATE);
222 #ifdef CONFIG_ROM_SIZE
223         t->bios_rom_size = (CONFIG_ROM_SIZE / 65536) - 1;
224 #endif
225         t->bios_characteristics = BIOS_CHARACTERISTICS_PCI_SUPPORTED |
226                                   BIOS_CHARACTERISTICS_SELECTABLE_BOOT |
227                                   BIOS_CHARACTERISTICS_UPGRADEABLE;
228 #ifdef CONFIG_GENERATE_ACPI_TABLE
229         t->bios_characteristics_ext1 = BIOS_CHARACTERISTICS_EXT1_ACPI;
230 #endif
231 #ifdef CONFIG_EFI_LOADER
232         t->bios_characteristics_ext1 |= BIOS_CHARACTERISTICS_EXT1_UEFI;
233 #endif
234         t->bios_characteristics_ext2 = BIOS_CHARACTERISTICS_EXT2_TARGET;
235
236         /* bios_major_release has only one byte, so drop century */
237         t->bios_major_release = U_BOOT_VERSION_NUM % 100;
238         t->bios_minor_release = U_BOOT_VERSION_NUM_PATCH;
239         t->ec_major_release = 0xff;
240         t->ec_minor_release = 0xff;
241
242         len = t->length + smbios_string_table_len(ctx);
243         *current += len;
244         unmap_sysmem(t);
245
246         return len;
247 }
248
249 static int smbios_write_type1(ulong *current, int handle,
250                               struct smbios_ctx *ctx)
251 {
252         struct smbios_type1 *t;
253         int len = sizeof(struct smbios_type1);
254         char *serial_str = env_get("serial#");
255
256         t = map_sysmem(*current, len);
257         memset(t, 0, sizeof(struct smbios_type1));
258         fill_smbios_header(t, SMBIOS_SYSTEM_INFORMATION, len, handle);
259         smbios_set_eos(ctx, t->eos);
260         t->manufacturer = smbios_add_prop(ctx, "manufacturer");
261         t->product_name = smbios_add_prop(ctx, "product");
262         t->version = smbios_add_prop_si(ctx, "version",
263                                         SYSINFO_ID_SMBIOS_SYSTEM_VERSION);
264         if (serial_str) {
265                 t->serial_number = smbios_add_string(ctx, serial_str);
266                 strncpy((char *)t->uuid, serial_str, sizeof(t->uuid));
267         } else {
268                 t->serial_number = smbios_add_prop(ctx, "serial");
269         }
270         t->sku_number = smbios_add_prop(ctx, "sku");
271         t->family = smbios_add_prop(ctx, "family");
272
273         len = t->length + smbios_string_table_len(ctx);
274         *current += len;
275         unmap_sysmem(t);
276
277         return len;
278 }
279
280 static int smbios_write_type2(ulong *current, int handle,
281                               struct smbios_ctx *ctx)
282 {
283         struct smbios_type2 *t;
284         int len = sizeof(struct smbios_type2);
285
286         t = map_sysmem(*current, len);
287         memset(t, 0, sizeof(struct smbios_type2));
288         fill_smbios_header(t, SMBIOS_BOARD_INFORMATION, len, handle);
289         smbios_set_eos(ctx, t->eos);
290         t->manufacturer = smbios_add_prop(ctx, "manufacturer");
291         t->product_name = smbios_add_prop(ctx, "product");
292         t->version = smbios_add_prop_si(ctx, "version",
293                                         SYSINFO_ID_SMBIOS_BASEBOARD_VERSION);
294         t->asset_tag_number = smbios_add_prop(ctx, "asset-tag");
295         t->feature_flags = SMBIOS_BOARD_FEATURE_HOSTING;
296         t->board_type = SMBIOS_BOARD_MOTHERBOARD;
297
298         len = t->length + smbios_string_table_len(ctx);
299         *current += len;
300         unmap_sysmem(t);
301
302         return len;
303 }
304
305 static int smbios_write_type3(ulong *current, int handle,
306                               struct smbios_ctx *ctx)
307 {
308         struct smbios_type3 *t;
309         int len = sizeof(struct smbios_type3);
310
311         t = map_sysmem(*current, len);
312         memset(t, 0, sizeof(struct smbios_type3));
313         fill_smbios_header(t, SMBIOS_SYSTEM_ENCLOSURE, len, handle);
314         smbios_set_eos(ctx, t->eos);
315         t->manufacturer = smbios_add_prop(ctx, "manufacturer");
316         t->chassis_type = SMBIOS_ENCLOSURE_DESKTOP;
317         t->bootup_state = SMBIOS_STATE_SAFE;
318         t->power_supply_state = SMBIOS_STATE_SAFE;
319         t->thermal_state = SMBIOS_STATE_SAFE;
320         t->security_status = SMBIOS_SECURITY_NONE;
321
322         len = t->length + smbios_string_table_len(ctx);
323         *current += len;
324         unmap_sysmem(t);
325
326         return len;
327 }
328
329 static void smbios_write_type4_dm(struct smbios_type4 *t,
330                                   struct smbios_ctx *ctx)
331 {
332         u16 processor_family = SMBIOS_PROCESSOR_FAMILY_UNKNOWN;
333         const char *vendor = "Unknown";
334         const char *name = "Unknown";
335
336 #ifdef CONFIG_CPU
337         char processor_name[49];
338         char vendor_name[49];
339         struct udevice *cpu = NULL;
340
341         uclass_find_first_device(UCLASS_CPU, &cpu);
342         if (cpu) {
343                 struct cpu_plat *plat = dev_get_parent_plat(cpu);
344
345                 if (plat->family)
346                         processor_family = plat->family;
347                 t->processor_id[0] = plat->id[0];
348                 t->processor_id[1] = plat->id[1];
349
350                 if (!cpu_get_vendor(cpu, vendor_name, sizeof(vendor_name)))
351                         vendor = vendor_name;
352                 if (!cpu_get_desc(cpu, processor_name, sizeof(processor_name)))
353                         name = processor_name;
354         }
355 #endif
356
357         t->processor_family = processor_family;
358         t->processor_manufacturer = smbios_add_string(ctx, vendor);
359         t->processor_version = smbios_add_string(ctx, name);
360 }
361
362 static int smbios_write_type4(ulong *current, int handle,
363                               struct smbios_ctx *ctx)
364 {
365         struct smbios_type4 *t;
366         int len = sizeof(struct smbios_type4);
367
368         t = map_sysmem(*current, len);
369         memset(t, 0, sizeof(struct smbios_type4));
370         fill_smbios_header(t, SMBIOS_PROCESSOR_INFORMATION, len, handle);
371         smbios_set_eos(ctx, t->eos);
372         t->processor_type = SMBIOS_PROCESSOR_TYPE_CENTRAL;
373         smbios_write_type4_dm(t, ctx);
374         t->status = SMBIOS_PROCESSOR_STATUS_ENABLED;
375         t->processor_upgrade = SMBIOS_PROCESSOR_UPGRADE_NONE;
376         t->l1_cache_handle = 0xffff;
377         t->l2_cache_handle = 0xffff;
378         t->l3_cache_handle = 0xffff;
379         t->processor_family2 = t->processor_family;
380
381         len = t->length + smbios_string_table_len(ctx);
382         *current += len;
383         unmap_sysmem(t);
384
385         return len;
386 }
387
388 static int smbios_write_type32(ulong *current, int handle,
389                                struct smbios_ctx *ctx)
390 {
391         struct smbios_type32 *t;
392         int len = sizeof(struct smbios_type32);
393
394         t = map_sysmem(*current, len);
395         memset(t, 0, sizeof(struct smbios_type32));
396         fill_smbios_header(t, SMBIOS_SYSTEM_BOOT_INFORMATION, len, handle);
397         smbios_set_eos(ctx, t->eos);
398
399         *current += len;
400         unmap_sysmem(t);
401
402         return len;
403 }
404
405 static int smbios_write_type127(ulong *current, int handle,
406                                 struct smbios_ctx *ctx)
407 {
408         struct smbios_type127 *t;
409         int len = sizeof(struct smbios_type127);
410
411         t = map_sysmem(*current, len);
412         memset(t, 0, sizeof(struct smbios_type127));
413         fill_smbios_header(t, SMBIOS_END_OF_TABLE, len, handle);
414
415         *current += len;
416         unmap_sysmem(t);
417
418         return len;
419 }
420
421 static struct smbios_write_method smbios_write_funcs[] = {
422         { smbios_write_type0, "bios", },
423         { smbios_write_type1, "system", },
424         { smbios_write_type2, "baseboard", },
425         { smbios_write_type3, "chassis", },
426         { smbios_write_type4, },
427         { smbios_write_type32, },
428         { smbios_write_type127 },
429 };
430
431 ulong write_smbios_table(ulong addr)
432 {
433         ofnode parent_node = ofnode_null();
434         struct smbios_entry *se;
435         struct smbios_ctx ctx;
436         ulong table_addr;
437         ulong tables;
438         int len = 0;
439         int max_struct_size = 0;
440         int handle = 0;
441         char *istart;
442         int isize;
443         int i;
444
445         ctx.node = ofnode_null();
446         if (IS_ENABLED(CONFIG_OF_CONTROL)) {
447                 uclass_first_device(UCLASS_SYSINFO, &ctx.dev);
448                 if (ctx.dev)
449                         parent_node = dev_read_subnode(ctx.dev, "smbios");
450         } else {
451                 ctx.dev = NULL;
452         }
453
454         /* 16 byte align the table address */
455         addr = ALIGN(addr, 16);
456
457         se = map_sysmem(addr, sizeof(struct smbios_entry));
458         memset(se, 0, sizeof(struct smbios_entry));
459
460         addr += sizeof(struct smbios_entry);
461         addr = ALIGN(addr, 16);
462         tables = addr;
463
464         /* populate minimum required tables */
465         for (i = 0; i < ARRAY_SIZE(smbios_write_funcs); i++) {
466                 const struct smbios_write_method *method;
467                 int tmp;
468
469                 method = &smbios_write_funcs[i];
470                 if (IS_ENABLED(CONFIG_OF_CONTROL) && method->subnode_name)
471                         ctx.node = ofnode_find_subnode(parent_node,
472                                                        method->subnode_name);
473                 tmp = method->write((ulong *)&addr, handle++, &ctx);
474
475                 max_struct_size = max(max_struct_size, tmp);
476                 len += tmp;
477         }
478
479         memcpy(se->anchor, "_SM_", 4);
480         se->length = sizeof(struct smbios_entry);
481         se->major_ver = SMBIOS_MAJOR_VER;
482         se->minor_ver = SMBIOS_MINOR_VER;
483         se->max_struct_size = max_struct_size;
484         memcpy(se->intermediate_anchor, "_DMI_", 5);
485         se->struct_table_length = len;
486
487         /*
488          * We must use a pointer here so things work correctly on sandbox. The
489          * user of this table is not aware of the mapping of addresses to
490          * sandbox's DRAM buffer.
491          */
492         table_addr = (ulong)map_sysmem(tables, 0);
493         if (sizeof(table_addr) > sizeof(u32) && table_addr > (ulong)UINT_MAX) {
494                 /*
495                  * We need to put this >32-bit pointer into the table but the
496                  * field is only 32 bits wide.
497                  */
498                 printf("WARNING: SMBIOS table_address overflow %llx\n",
499                        (unsigned long long)table_addr);
500                 table_addr = 0;
501         }
502         se->struct_table_address = table_addr;
503
504         se->struct_count = handle;
505
506         /* calculate checksums */
507         istart = (char *)se + SMBIOS_INTERMEDIATE_OFFSET;
508         isize = sizeof(struct smbios_entry) - SMBIOS_INTERMEDIATE_OFFSET;
509         se->intermediate_checksum = table_compute_checksum(istart, isize);
510         se->checksum = table_compute_checksum(se, sizeof(struct smbios_entry));
511         unmap_sysmem(se);
512
513         return addr;
514 }