Files
u-boot/lib/smbios-parser.c
Simon Glass 4e842608d6 smbios: Update smbios_get_header() to return a void *
Since each table starts with a header, it isn't very useful to have a
separate pointer for the (generic) header and another for the whole
table. Also, casting is a bit of a pain.

Update smbios_get_header() so that it returns a const void * so that it
is possible to directly assign it to the appropriate SMBIOS table's
pointer.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-09-04 07:08:24 -06:00

313 lines
8.0 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2020, Bachmann electronic GmbH
*/
#define LOG_CATEGORY LOGC_BOOT
#include <errno.h>
#include <mapmem.h>
#include <smbios.h>
#include <string.h>
#include <tables_csum.h>
#include <asm/global_data.h>
#include <linux/kernel.h>
DECLARE_GLOBAL_DATA_PTR;
const char *smbios_get_string(void *table, int index)
{
const char *str = (char *)table +
((struct smbios_header *)table)->length;
static const char fallback[] = "";
if (!index)
return fallback;
if (!*str)
++str;
for (--index; *str && index; --index)
str += strlen(str) + 1;
return str;
}
struct smbios_header *smbios_next_table(const struct smbios_info *info,
struct smbios_header *table)
{
const char *str;
if ((ulong)table - (ulong)info->table >= info->max_size)
return NULL;
if (table->type == SMBIOS_END_OF_TABLE)
return NULL;
str = smbios_get_string(table, -1);
return (struct smbios_header *)(++str);
}
const struct smbios_entry *smbios_entry(u64 address, u32 size)
{
const struct smbios_entry *entry = (struct smbios_entry *)(uintptr_t)address;
if (!address || !size)
return NULL;
if (memcmp(entry->anchor, "_SM_", 4))
return NULL;
if (table_compute_checksum(entry, entry->length))
return NULL;
return entry;
}
const void *smbios_get_header(const struct smbios_info *info, int type)
{
struct smbios_header *header;
for (header = info->table; header;
header = smbios_next_table(info, header)) {
if (header->type == type)
return header;
}
return NULL;
}
static char *string_from_smbios_table(const struct smbios_header *header,
int idx)
{
unsigned int i = 1;
u8 *pos;
if (!header)
return NULL;
pos = ((u8 *)header) + header->length;
while (i < idx) {
if (*pos == 0x0)
i++;
pos++;
}
return (char *)pos;
}
char *smbios_string(const struct smbios_header *header, int index)
{
if (!header)
return NULL;
return string_from_smbios_table(header, index);
}
int smbios_update_version_full(void *smbios_tab, const char *new_version)
{
const struct smbios_type0 *bios;
struct smbios_info info;
uint old_len, len;
char *ptr;
int ret;
ret = smbios_locate(map_to_sysmem(smbios_tab), &info);
if (ret)
return log_msg_ret("tab", -ENOENT);
log_info("Updating SMBIOS table at %p\n", smbios_tab);
bios = smbios_get_header(&info, SMBIOS_BIOS_INFORMATION);
if (!bios)
return log_msg_ret("tab", -ENOENT);
ptr = smbios_string(&bios->hdr, bios->bios_ver);
if (!ptr)
return log_msg_ret("str", -ENOMEDIUM);
/*
* This string is supposed to have at least enough bytes and is
* padded with spaces. Update it, taking care not to move the
* \0 terminator, so that other strings in the string table
* are not disturbed. See smbios_add_string()
*/
old_len = strnlen(ptr, SMBIOS_STR_MAX);
len = strnlen(new_version, SMBIOS_STR_MAX);
if (len > old_len)
return log_ret(-ENOSPC);
log_debug("Replacing SMBIOS type 0 version string '%s'\n", ptr);
memcpy(ptr, new_version, len);
#ifdef LOG_DEBUG
print_buffer((ulong)ptr, ptr, 1, old_len + 1, 0);
#endif
return 0;
}
struct smbios_filter_param {
u32 offset;
u32 size;
bool is_string;
};
struct smbios_filter_table {
int type;
struct smbios_filter_param *params;
u32 count;
};
struct smbios_filter_param smbios_type1_filter_params[] = {
{offsetof(struct smbios_type1, serial_number),
FIELD_SIZEOF(struct smbios_type1, serial_number), true},
{offsetof(struct smbios_type1, uuid),
FIELD_SIZEOF(struct smbios_type1, uuid), false},
{offsetof(struct smbios_type1, wakeup_type),
FIELD_SIZEOF(struct smbios_type1, wakeup_type), false},
};
struct smbios_filter_param smbios_type2_filter_params[] = {
{offsetof(struct smbios_type2, serial_number),
FIELD_SIZEOF(struct smbios_type2, serial_number), true},
{offsetof(struct smbios_type2, chassis_location),
FIELD_SIZEOF(struct smbios_type2, chassis_location), false},
};
struct smbios_filter_param smbios_type3_filter_params[] = {
{offsetof(struct smbios_type3, serial_number),
FIELD_SIZEOF(struct smbios_type3, serial_number), true},
{offsetof(struct smbios_type3, asset_tag_number),
FIELD_SIZEOF(struct smbios_type3, asset_tag_number), true},
};
struct smbios_filter_param smbios_type4_filter_params[] = {
{offsetof(struct smbios_type4, serial_number),
FIELD_SIZEOF(struct smbios_type4, serial_number), true},
{offsetof(struct smbios_type4, asset_tag),
FIELD_SIZEOF(struct smbios_type4, asset_tag), true},
{offsetof(struct smbios_type4, part_number),
FIELD_SIZEOF(struct smbios_type4, part_number), true},
{offsetof(struct smbios_type4, core_count),
FIELD_SIZEOF(struct smbios_type4, core_count), false},
{offsetof(struct smbios_type4, core_enabled),
FIELD_SIZEOF(struct smbios_type4, core_enabled), false},
{offsetof(struct smbios_type4, thread_count),
FIELD_SIZEOF(struct smbios_type4, thread_count), false},
{offsetof(struct smbios_type4, core_count2),
FIELD_SIZEOF(struct smbios_type4, core_count2), false},
{offsetof(struct smbios_type4, core_enabled2),
FIELD_SIZEOF(struct smbios_type4, core_enabled2), false},
{offsetof(struct smbios_type4, thread_count2),
FIELD_SIZEOF(struct smbios_type4, thread_count2), false},
{offsetof(struct smbios_type4, voltage),
FIELD_SIZEOF(struct smbios_type4, voltage), false},
};
struct smbios_filter_table smbios_filter_tables[] = {
{SMBIOS_SYSTEM_INFORMATION, smbios_type1_filter_params,
ARRAY_SIZE(smbios_type1_filter_params)},
{SMBIOS_BOARD_INFORMATION, smbios_type2_filter_params,
ARRAY_SIZE(smbios_type2_filter_params)},
{SMBIOS_SYSTEM_ENCLOSURE, smbios_type3_filter_params,
ARRAY_SIZE(smbios_type3_filter_params)},
{SMBIOS_PROCESSOR_INFORMATION, smbios_type4_filter_params,
ARRAY_SIZE(smbios_type4_filter_params)},
};
static void clear_smbios_table(struct smbios_header *header,
struct smbios_filter_param *filter,
u32 count)
{
u32 i;
char *str;
u8 string_id;
for (i = 0; i < count; i++) {
if (filter[i].is_string) {
string_id = *((u8 *)header + filter[i].offset);
if (string_id == 0) /* string is empty */
continue;
str = smbios_string(header, string_id);
if (!str)
continue;
/* string is cleared to space, keep '\0' terminator */
memset(str, ' ', strlen(str));
} else {
memset((void *)((u8 *)header + filter[i].offset),
0, filter[i].size);
}
}
}
void smbios_prepare_measurement(const struct smbios3_entry *entry,
struct smbios_header *smbios_copy,
int table_maximum_size)
{
struct smbios_info info;
u32 i;
info.table = smbios_copy;
info.count = 0; /* unknown */
info.max_size = table_maximum_size;
info.version = 3 << 16;
for (i = 0; i < ARRAY_SIZE(smbios_filter_tables); i++) {
struct smbios_header *header;
for (header = info.table; header;
header = smbios_next_table(&info, header)) {
if (header->type == smbios_filter_tables[i].type)
break;
}
if (!header)
continue;
clear_smbios_table(header,
smbios_filter_tables[i].params,
smbios_filter_tables[i].count);
}
}
int smbios_locate(ulong addr, struct smbios_info *info)
{
static const char smbios3_sig[] = "_SM3_";
static const char smbios_sig[] = "_SM_";
void *entry;
uint size;
if (!addr)
return -EINVAL;
entry = map_sysmem(addr, 0);
if (!memcmp(entry, smbios3_sig, sizeof(smbios3_sig) - 1)) {
struct smbios3_entry *entry3 = entry;
info->table = (void *)(uintptr_t)entry3->struct_table_address;
info->version = entry3->major_ver << 16 |
entry3->minor_ver << 8 | entry3->doc_rev;
size = entry3->length;
info->max_size = entry3->table_maximum_size;
} else if (!memcmp(entry, smbios_sig, sizeof(smbios_sig) - 1)) {
struct smbios_entry *entry2 = entry;
info->version = entry2->major_ver << 16 |
entry2->minor_ver << 8;
info->table = (void *)(uintptr_t)entry2->struct_table_address;
size = entry2->length;
info->max_size = entry2->struct_table_length;
} else {
return -ENOENT;
}
if (table_compute_checksum(entry, size))
return -EIO;
info->count = 0;
for (struct smbios_header *pos = info->table; pos;
pos = smbios_next_table(info, pos))
info->count++;
return 0;
}