Commit 721e992a8af5e80b2a95a0bc92c9880f2056190b

Authored by Bin Meng
Committed by Simon Glass
1 parent a34b46768f

x86: Add SMBIOS table support

System Management BIOS (SMBIOS) is a specification for how
motherboard and system vendors present management information
about their products in a standard format by extending the BIOS
interface on Intel architecture systems. As of today the latest
spec is 3.0 and can be downloaded from DMTF website. This commit
adds a simple and minimum required implementation.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Acked-by: Simon Glass <sjg@chromium.org>

Showing 6 changed files with 522 additions and 2 deletions Side-by-side Diff

... ... @@ -358,6 +358,17 @@
358 358 by the operating system. It defines platform-independent interfaces
359 359 for configuration and power management monitoring.
360 360  
  361 +config GENERATE_SMBIOS_TABLE
  362 + bool "Generate an SMBIOS (System Management BIOS) table"
  363 + default y
  364 + help
  365 + The System Management BIOS (SMBIOS) specification addresses how
  366 + motherboard and system vendors present management information about
  367 + their products in a standard format by extending the BIOS interface
  368 + on Intel architecture systems.
  369 +
  370 + Check http://www.dmtf.org/standards/smbios for details.
  371 +
361 372 endmenu
362 373  
363 374 config MAX_PIRQ_LINKS
arch/x86/include/asm/smbios.h
  1 +/*
  2 + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
  3 + *
  4 + * Adapted from coreboot src/include/smbios.h
  5 + *
  6 + * SPDX-License-Identifier: GPL-2.0+
  7 + */
  8 +
  9 +#ifndef _SMBIOS_H_
  10 +#define _SMBIOS_H_
  11 +
  12 +/* SMBIOS spec version implemented */
  13 +#define SMBIOS_MAJOR_VER 3
  14 +#define SMBIOS_MINOR_VER 0
  15 +
  16 +/* SMBIOS structure types */
  17 +enum {
  18 + SMBIOS_BIOS_INFORMATION = 0,
  19 + SMBIOS_SYSTEM_INFORMATION = 1,
  20 + SMBIOS_BOARD_INFORMATION = 2,
  21 + SMBIOS_SYSTEM_ENCLOSURE = 3,
  22 + SMBIOS_PROCESSOR_INFORMATION = 4,
  23 + SMBIOS_CACHE_INFORMATION = 7,
  24 + SMBIOS_SYSTEM_SLOTS = 9,
  25 + SMBIOS_PHYS_MEMORY_ARRAY = 16,
  26 + SMBIOS_MEMORY_DEVICE = 17,
  27 + SMBIOS_MEMORY_ARRAY_MAPPED_ADDRESS = 19,
  28 + SMBIOS_SYSTEM_BOOT_INFORMATION = 32,
  29 + SMBIOS_END_OF_TABLE = 127
  30 +};
  31 +
  32 +#define SMBIOS_INTERMEDIATE_OFFSET 16
  33 +#define SMBIOS_STRUCT_EOS_BYTES 2
  34 +
  35 +struct __packed smbios_entry {
  36 + u8 anchor[4];
  37 + u8 checksum;
  38 + u8 length;
  39 + u8 major_ver;
  40 + u8 minor_ver;
  41 + u16 max_struct_size;
  42 + u8 entry_point_rev;
  43 + u8 formatted_area[5];
  44 + u8 intermediate_anchor[5];
  45 + u8 intermediate_checksum;
  46 + u16 struct_table_length;
  47 + u32 struct_table_address;
  48 + u16 struct_count;
  49 + u8 bcd_rev;
  50 +};
  51 +
  52 +/* BIOS characteristics */
  53 +#define BIOS_CHARACTERISTICS_PCI_SUPPORTED (1 << 7)
  54 +#define BIOS_CHARACTERISTICS_UPGRADEABLE (1 << 11)
  55 +#define BIOS_CHARACTERISTICS_SELECTABLE_BOOT (1 << 16)
  56 +
  57 +#define BIOS_CHARACTERISTICS_EXT1_ACPI (1 << 0)
  58 +#define BIOS_CHARACTERISTICS_EXT2_TARGET (1 << 2)
  59 +
  60 +struct __packed smbios_type0 {
  61 + u8 type;
  62 + u8 length;
  63 + u16 handle;
  64 + u8 vendor;
  65 + u8 bios_ver;
  66 + u16 bios_start_segment;
  67 + u8 bios_release_date;
  68 + u8 bios_rom_size;
  69 + u64 bios_characteristics;
  70 + u8 bios_characteristics_ext1;
  71 + u8 bios_characteristics_ext2;
  72 + u8 bios_major_release;
  73 + u8 bios_minor_release;
  74 + u8 ec_major_release;
  75 + u8 ec_minor_release;
  76 + char eos[SMBIOS_STRUCT_EOS_BYTES];
  77 +};
  78 +
  79 +struct __packed smbios_type1 {
  80 + u8 type;
  81 + u8 length;
  82 + u16 handle;
  83 + u8 manufacturer;
  84 + u8 product_name;
  85 + u8 version;
  86 + u8 serial_number;
  87 + u8 uuid[16];
  88 + u8 wakeup_type;
  89 + u8 sku_number;
  90 + u8 family;
  91 + char eos[SMBIOS_STRUCT_EOS_BYTES];
  92 +};
  93 +
  94 +#define SMBIOS_BOARD_FEATURE_HOSTING (1 << 0)
  95 +#define SMBIOS_BOARD_MOTHERBOARD 10
  96 +
  97 +struct __packed smbios_type2 {
  98 + u8 type;
  99 + u8 length;
  100 + u16 handle;
  101 + u8 manufacturer;
  102 + u8 product_name;
  103 + u8 version;
  104 + u8 serial_number;
  105 + u8 asset_tag_number;
  106 + u8 feature_flags;
  107 + u8 chassis_location;
  108 + u16 chassis_handle;
  109 + u8 board_type;
  110 + char eos[SMBIOS_STRUCT_EOS_BYTES];
  111 +};
  112 +
  113 +#define SMBIOS_ENCLOSURE_DESKTOP 3
  114 +#define SMBIOS_STATE_SAFE 3
  115 +#define SMBIOS_SECURITY_NONE 3
  116 +
  117 +struct __packed smbios_type3 {
  118 + u8 type;
  119 + u8 length;
  120 + u16 handle;
  121 + u8 manufacturer;
  122 + u8 chassis_type;
  123 + u8 version;
  124 + u8 serial_number;
  125 + u8 asset_tag_number;
  126 + u8 bootup_state;
  127 + u8 power_supply_state;
  128 + u8 thermal_state;
  129 + u8 security_status;
  130 + u32 oem_defined;
  131 + u8 height;
  132 + u8 number_of_power_cords;
  133 + u8 element_count;
  134 + u8 element_record_length;
  135 + char eos[SMBIOS_STRUCT_EOS_BYTES];
  136 +};
  137 +
  138 +#define SMBIOS_PROCESSOR_TYPE_CENTRAL 3
  139 +#define SMBIOS_PROCESSOR_STATUS_ENABLED 1
  140 +#define SMBIOS_PROCESSOR_UPGRADE_NONE 6
  141 +
  142 +struct __packed smbios_type4 {
  143 + u8 type;
  144 + u8 length;
  145 + u16 handle;
  146 + u8 socket_designation;
  147 + u8 processor_type;
  148 + u8 processor_family;
  149 + u8 processor_manufacturer;
  150 + u32 processor_id[2];
  151 + u8 processor_version;
  152 + u8 voltage;
  153 + u16 external_clock;
  154 + u16 max_speed;
  155 + u16 current_speed;
  156 + u8 status;
  157 + u8 processor_upgrade;
  158 + u16 l1_cache_handle;
  159 + u16 l2_cache_handle;
  160 + u16 l3_cache_handle;
  161 + u8 serial_number;
  162 + u8 asset_tag;
  163 + u8 part_number;
  164 + u8 core_count;
  165 + u8 core_enabled;
  166 + u8 thread_count;
  167 + u16 processor_characteristics;
  168 + u16 processor_family2;
  169 + u16 core_count2;
  170 + u16 core_enabled2;
  171 + u16 thread_count2;
  172 + char eos[SMBIOS_STRUCT_EOS_BYTES];
  173 +};
  174 +
  175 +struct __packed smbios_type32 {
  176 + u8 type;
  177 + u8 length;
  178 + u16 handle;
  179 + u8 reserved[6];
  180 + u8 boot_status;
  181 + u8 eos[SMBIOS_STRUCT_EOS_BYTES];
  182 +};
  183 +
  184 +struct __packed smbios_type127 {
  185 + u8 type;
  186 + u8 length;
  187 + u16 handle;
  188 + u8 eos[SMBIOS_STRUCT_EOS_BYTES];
  189 +};
  190 +
  191 +struct __packed smbios_header {
  192 + u8 type;
  193 + u8 length;
  194 + u16 handle;
  195 +};
  196 +
  197 +/**
  198 + * fill_smbios_header() - Fill the header of an SMBIOS table
  199 + *
  200 + * This fills the header of an SMBIOS table structure.
  201 + *
  202 + * @table: start address of the structure
  203 + * @type: the type of structure
  204 + * @length: the length of the formatted area of the structure
  205 + * @handle: the structure's handle, a unique 16-bit number
  206 + */
  207 +static inline void fill_smbios_header(void *table, int type,
  208 + int length, int handle)
  209 +{
  210 + struct smbios_header *header = table;
  211 +
  212 + header->type = type;
  213 + header->length = length - SMBIOS_STRUCT_EOS_BYTES;
  214 + header->handle = handle;
  215 +}
  216 +
  217 +/**
  218 + * Function prototype to write a specific type of SMBIOS structure
  219 + *
  220 + * @addr: start address to write the structure
  221 + * @handle: the structure's handle, a unique 16-bit number
  222 + * @return: size of the structure
  223 + */
  224 +typedef int (*smbios_write_type)(u32 *addr, int handle);
  225 +
  226 +/**
  227 + * write_smbios_table() - Write SMBIOS table
  228 + *
  229 + * This writes SMBIOS table at a given address.
  230 + *
  231 + * @addr: start address to write SMBIOS table
  232 + * @return: end address of SMBIOS table
  233 + */
  234 +u32 write_smbios_table(u32 addr);
  235 +
  236 +#endif /* _SMBIOS_H_ */
arch/x86/lib/Makefile
... ... @@ -30,6 +30,7 @@
30 30 obj-y += physmem.o
31 31 obj-$(CONFIG_X86_RAMTEST) += ramtest.o
32 32 obj-y += sfi.o
  33 +obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += smbios.o
33 34 obj-y += string.o
34 35 obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi_table.o
35 36 obj-y += tables.o
arch/x86/lib/smbios.c
  1 +/*
  2 + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
  3 + *
  4 + * Adapted from coreboot src/arch/x86/smbios.c
  5 + *
  6 + * SPDX-License-Identifier: GPL-2.0+
  7 + */
  8 +
  9 +#include <common.h>
  10 +#include <version.h>
  11 +#include <asm/cpu.h>
  12 +#include <asm/smbios.h>
  13 +#include <asm/tables.h>
  14 +
  15 +DECLARE_GLOBAL_DATA_PTR;
  16 +
  17 +/**
  18 + * smbios_add_string() - add a string to the string area
  19 + *
  20 + * This adds a string to the string area which is appended directly after
  21 + * the formatted portion of an SMBIOS structure.
  22 + *
  23 + * @start: string area start address
  24 + * @str: string to add
  25 + * @return: string number in the string area
  26 + */
  27 +static int smbios_add_string(char *start, const char *str)
  28 +{
  29 + int i = 1;
  30 + char *p = start;
  31 +
  32 + for (;;) {
  33 + if (!*p) {
  34 + strcpy(p, str);
  35 + p += strlen(str);
  36 + *p++ = '\0';
  37 + *p++ = '\0';
  38 +
  39 + return i;
  40 + }
  41 +
  42 + if (!strcmp(p, str))
  43 + return i;
  44 +
  45 + p += strlen(p) + 1;
  46 + i++;
  47 + }
  48 +}
  49 +
  50 +/**
  51 + * smbios_string_table_len() - compute the string area size
  52 + *
  53 + * This computes the size of the string area including the string terminator.
  54 + *
  55 + * @start: string area start address
  56 + * @return: string area size
  57 + */
  58 +static int smbios_string_table_len(char *start)
  59 +{
  60 + char *p = start;
  61 + int i, len = 0;
  62 +
  63 + while (*p) {
  64 + i = strlen(p) + 1;
  65 + p += i;
  66 + len += i;
  67 + }
  68 +
  69 + return len + 1;
  70 +}
  71 +
  72 +static int smbios_write_type0(u32 *current, int handle)
  73 +{
  74 + struct smbios_type0 *t = (struct smbios_type0 *)*current;
  75 + int len = sizeof(struct smbios_type0);
  76 +
  77 + memset(t, 0, sizeof(struct smbios_type0));
  78 + fill_smbios_header(t, SMBIOS_BIOS_INFORMATION, len, handle);
  79 + t->vendor = smbios_add_string(t->eos, "U-Boot");
  80 + t->bios_ver = smbios_add_string(t->eos, PLAIN_VERSION);
  81 + t->bios_release_date = smbios_add_string(t->eos, U_BOOT_DMI_DATE);
  82 + t->bios_rom_size = (CONFIG_ROM_SIZE / 65536) - 1;
  83 + t->bios_characteristics = BIOS_CHARACTERISTICS_PCI_SUPPORTED |
  84 + BIOS_CHARACTERISTICS_SELECTABLE_BOOT |
  85 + BIOS_CHARACTERISTICS_UPGRADEABLE;
  86 +#ifdef CONFIG_GENERATE_ACPI_TABLE
  87 + t->bios_characteristics_ext1 = BIOS_CHARACTERISTICS_EXT1_ACPI;
  88 +#endif
  89 + t->bios_characteristics_ext2 = BIOS_CHARACTERISTICS_EXT2_TARGET;
  90 + t->bios_major_release = 0xff;
  91 + t->bios_minor_release = 0xff;
  92 + t->ec_major_release = 0xff;
  93 + t->ec_minor_release = 0xff;
  94 +
  95 + len = t->length + smbios_string_table_len(t->eos);
  96 + *current += len;
  97 +
  98 + return len;
  99 +}
  100 +
  101 +static int smbios_write_type1(u32 *current, int handle)
  102 +{
  103 + struct smbios_type1 *t = (struct smbios_type1 *)*current;
  104 + int len = sizeof(struct smbios_type1);
  105 +
  106 + memset(t, 0, sizeof(struct smbios_type1));
  107 + fill_smbios_header(t, SMBIOS_SYSTEM_INFORMATION, len, handle);
  108 + t->manufacturer = smbios_add_string(t->eos, CONFIG_SYS_VENDOR);
  109 + t->product_name = smbios_add_string(t->eos, CONFIG_SYS_BOARD);
  110 +
  111 + len = t->length + smbios_string_table_len(t->eos);
  112 + *current += len;
  113 +
  114 + return len;
  115 +}
  116 +
  117 +static int smbios_write_type2(u32 *current, int handle)
  118 +{
  119 + struct smbios_type2 *t = (struct smbios_type2 *)*current;
  120 + int len = sizeof(struct smbios_type2);
  121 +
  122 + memset(t, 0, sizeof(struct smbios_type2));
  123 + fill_smbios_header(t, SMBIOS_BOARD_INFORMATION, len, handle);
  124 + t->manufacturer = smbios_add_string(t->eos, CONFIG_SYS_VENDOR);
  125 + t->product_name = smbios_add_string(t->eos, CONFIG_SYS_BOARD);
  126 + t->feature_flags = SMBIOS_BOARD_FEATURE_HOSTING;
  127 + t->board_type = SMBIOS_BOARD_MOTHERBOARD;
  128 +
  129 + len = t->length + smbios_string_table_len(t->eos);
  130 + *current += len;
  131 +
  132 + return len;
  133 +}
  134 +
  135 +static int smbios_write_type3(u32 *current, int handle)
  136 +{
  137 + struct smbios_type3 *t = (struct smbios_type3 *)*current;
  138 + int len = sizeof(struct smbios_type3);
  139 +
  140 + memset(t, 0, sizeof(struct smbios_type3));
  141 + fill_smbios_header(t, SMBIOS_SYSTEM_ENCLOSURE, len, handle);
  142 + t->manufacturer = smbios_add_string(t->eos, CONFIG_SYS_VENDOR);
  143 + t->chassis_type = SMBIOS_ENCLOSURE_DESKTOP;
  144 + t->bootup_state = SMBIOS_STATE_SAFE;
  145 + t->power_supply_state = SMBIOS_STATE_SAFE;
  146 + t->thermal_state = SMBIOS_STATE_SAFE;
  147 + t->security_status = SMBIOS_SECURITY_NONE;
  148 +
  149 + len = t->length + smbios_string_table_len(t->eos);
  150 + *current += len;
  151 +
  152 + return len;
  153 +}
  154 +
  155 +static int smbios_write_type4(u32 *current, int handle)
  156 +{
  157 + struct smbios_type4 *t = (struct smbios_type4 *)*current;
  158 + int len = sizeof(struct smbios_type4);
  159 + const char *vendor;
  160 + char *name;
  161 + char processor_name[CPU_MAX_NAME_LEN];
  162 + struct cpuid_result res;
  163 +
  164 + memset(t, 0, sizeof(struct smbios_type4));
  165 + fill_smbios_header(t, SMBIOS_PROCESSOR_INFORMATION, len, handle);
  166 + t->processor_type = SMBIOS_PROCESSOR_TYPE_CENTRAL;
  167 + t->processor_family = gd->arch.x86;
  168 + vendor = cpu_vendor_name(gd->arch.x86_vendor);
  169 + t->processor_manufacturer = smbios_add_string(t->eos, vendor);
  170 + res = cpuid(1);
  171 + t->processor_id[0] = res.eax;
  172 + t->processor_id[1] = res.edx;
  173 + name = cpu_get_name(processor_name);
  174 + t->processor_version = smbios_add_string(t->eos, name);
  175 + t->status = SMBIOS_PROCESSOR_STATUS_ENABLED;
  176 + t->processor_upgrade = SMBIOS_PROCESSOR_UPGRADE_NONE;
  177 + t->l1_cache_handle = 0xffff;
  178 + t->l2_cache_handle = 0xffff;
  179 + t->l3_cache_handle = 0xffff;
  180 + t->processor_family2 = t->processor_family;
  181 +
  182 + len = t->length + smbios_string_table_len(t->eos);
  183 + *current += len;
  184 +
  185 + return len;
  186 +}
  187 +
  188 +static int smbios_write_type32(u32 *current, int handle)
  189 +{
  190 + struct smbios_type32 *t = (struct smbios_type32 *)*current;
  191 + int len = sizeof(struct smbios_type32);
  192 +
  193 + memset(t, 0, sizeof(struct smbios_type32));
  194 + fill_smbios_header(t, SMBIOS_SYSTEM_BOOT_INFORMATION, len, handle);
  195 +
  196 + *current += len;
  197 +
  198 + return len;
  199 +}
  200 +
  201 +static int smbios_write_type127(u32 *current, int handle)
  202 +{
  203 + struct smbios_type127 *t = (struct smbios_type127 *)*current;
  204 + int len = sizeof(struct smbios_type127);
  205 +
  206 + memset(t, 0, sizeof(struct smbios_type127));
  207 + fill_smbios_header(t, SMBIOS_END_OF_TABLE, len, handle);
  208 +
  209 + *current += len;
  210 +
  211 + return len;
  212 +}
  213 +
  214 +static smbios_write_type smbios_write_funcs[] = {
  215 + smbios_write_type0,
  216 + smbios_write_type1,
  217 + smbios_write_type2,
  218 + smbios_write_type3,
  219 + smbios_write_type4,
  220 + smbios_write_type32,
  221 + smbios_write_type127
  222 +};
  223 +
  224 +u32 write_smbios_table(u32 addr)
  225 +{
  226 + struct smbios_entry *se;
  227 + u32 tables;
  228 + int len = 0;
  229 + int max_struct_size = 0;
  230 + int handle = 0;
  231 + char *istart;
  232 + int isize;
  233 + int i;
  234 +
  235 + /* 16 byte align the table address */
  236 + addr = ALIGN(addr, 16);
  237 +
  238 + se = (struct smbios_entry *)addr;
  239 + memset(se, 0, sizeof(struct smbios_entry));
  240 +
  241 + addr += sizeof(struct smbios_entry);
  242 + addr = ALIGN(addr, 16);
  243 + tables = addr;
  244 +
  245 + /* populate minimum required tables */
  246 + for (i = 0; i < ARRAY_SIZE(smbios_write_funcs); i++) {
  247 + int tmp = smbios_write_funcs[i](&addr, handle++);
  248 + max_struct_size = max(max_struct_size, tmp);
  249 + len += tmp;
  250 + }
  251 +
  252 + memcpy(se->anchor, "_SM_", 4);
  253 + se->length = sizeof(struct smbios_entry);
  254 + se->major_ver = SMBIOS_MAJOR_VER;
  255 + se->minor_ver = SMBIOS_MINOR_VER;
  256 + se->max_struct_size = max_struct_size;
  257 + memcpy(se->intermediate_anchor, "_DMI_", 5);
  258 + se->struct_table_length = len;
  259 + se->struct_table_address = tables;
  260 + se->struct_count = handle;
  261 +
  262 + /* calculate checksums */
  263 + istart = (char *)se + SMBIOS_INTERMEDIATE_OFFSET;
  264 + isize = sizeof(struct smbios_entry) - SMBIOS_INTERMEDIATE_OFFSET;
  265 + se->intermediate_checksum = table_compute_checksum(istart, isize);
  266 + se->checksum = table_compute_checksum(se, sizeof(struct smbios_entry));
  267 +
  268 + return addr;
  269 +}
arch/x86/lib/tables.c
... ... @@ -7,6 +7,7 @@
7 7 #include <common.h>
8 8 #include <asm/sfi.h>
9 9 #include <asm/mpspec.h>
  10 +#include <asm/smbios.h>
10 11 #include <asm/tables.h>
11 12 #include <asm/acpi_table.h>
12 13  
... ... @@ -54,6 +55,10 @@
54 55 #endif
55 56 #ifdef CONFIG_GENERATE_ACPI_TABLE
56 57 rom_table_end = write_acpi_tables(rom_table_end);
  58 + rom_table_end = ALIGN(rom_table_end, 1024);
  59 +#endif
  60 +#ifdef CONFIG_GENERATE_SMBIOS_TABLE
  61 + rom_table_end = write_smbios_table(rom_table_end);
57 62 rom_table_end = ALIGN(rom_table_end, 1024);
58 63 #endif
59 64 }
... ... @@ -764,7 +764,6 @@
764 764 - Audio
765 765 - Chrome OS verified boot
766 766 - SMI and ACPI support, to provide platform info and facilities to Linux
767   -- Desktop Management Interface (DMI) [15] support
768 767  
769 768 References
770 769 ----------
... ... @@ -782,5 +781,4 @@
782 781 [12] http://events.linuxfoundation.org/sites/events/files/slides/chromeos_and_diy_vboot_0.pdf
783 782 [13] http://events.linuxfoundation.org/sites/events/files/slides/elce-2014.pdf
784 783 [14] doc/device-tree-bindings/misc/intel,irq-router.txt
785   -[15] http://en.wikipedia.org/wiki/Desktop_Management_Interface