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