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