blob: 8782a1618d3a0221f5c8d1b4b1d3b5b23c09f8de [file] [log] [blame]
#include <assert.h>
#include <endian.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "bios.h"
#include "bios_table.h"
#define POINTER_CHECK(pointer) \
do { \
if (pointer == NULL) \
return PLDM_ERROR_INVALID_DATA; \
} while (0)
#define ATTR_TYPE_EXPECT(type, expected) \
do { \
if (type != expected && type != (expected | 0x80)) \
return PLDM_ERROR_INVALID_DATA; \
} while (0)
#define BUFFER_SIZE_EXPECT(current_size, expected_size) \
do { \
if (current_size < expected_size) \
return PLDM_ERROR_INVALID_LENGTH; \
} while (0)
#define MEMBER_SIZE(type, member) sizeof(((struct type *)0)->member)
static uint16_t get_bios_string_handle()
{
static uint16_t handle = 0;
assert(handle != UINT16_MAX);
return handle++;
}
size_t pldm_bios_table_string_entry_encode_length(uint16_t string_length)
{
return sizeof(struct pldm_bios_string_table_entry) -
MEMBER_SIZE(pldm_bios_string_table_entry, name) + string_length;
}
void pldm_bios_table_string_entry_encode(void *entry, size_t entry_length,
const char *str, uint16_t str_length)
{
size_t length = pldm_bios_table_string_entry_encode_length(str_length);
assert(length <= entry_length);
struct pldm_bios_string_table_entry *string_entry = entry;
string_entry->string_handle = htole16(get_bios_string_handle());
string_entry->string_length = htole16(str_length);
memcpy(string_entry->name, str, str_length);
}
int pldm_bios_table_string_entry_encode_check(void *entry, size_t entry_length,
const char *str,
uint16_t str_length)
{
if (str_length == 0)
return PLDM_ERROR_INVALID_DATA;
POINTER_CHECK(entry);
POINTER_CHECK(str);
size_t length = pldm_bios_table_string_entry_encode_length(str_length);
BUFFER_SIZE_EXPECT(entry_length, length);
pldm_bios_table_string_entry_encode(entry, entry_length, str,
str_length);
return PLDM_SUCCESS;
}
uint16_t pldm_bios_table_string_entry_decode_handle(
const struct pldm_bios_string_table_entry *entry)
{
return le16toh(entry->string_handle);
}
uint16_t pldm_bios_table_string_entry_decode_string_length(
const struct pldm_bios_string_table_entry *entry)
{
return le16toh(entry->string_length);
}
uint16_t pldm_bios_table_string_entry_decode_string(
const struct pldm_bios_string_table_entry *entry, char *buffer, size_t size)
{
uint16_t length =
pldm_bios_table_string_entry_decode_string_length(entry);
length = length < size ? length : size;
memcpy(buffer, entry->name, length);
buffer[length] = 0;
return length;
}
int pldm_bios_table_string_entry_decode_string_check(
const struct pldm_bios_string_table_entry *entry, char *buffer, size_t size)
{
POINTER_CHECK(entry);
POINTER_CHECK(buffer);
size_t length =
pldm_bios_table_string_entry_decode_string_length(entry);
BUFFER_SIZE_EXPECT(size, length + 1);
pldm_bios_table_string_entry_decode_string(entry, buffer, size);
return PLDM_SUCCESS;
}
static size_t string_table_entry_length(const void *table_entry)
{
const struct pldm_bios_string_table_entry *entry = table_entry;
return sizeof(*entry) - sizeof(entry->name) +
pldm_bios_table_string_entry_decode_string_length(entry);
}
static uint16_t get_bios_attr_handle()
{
static uint16_t handle = 0;
assert(handle != UINT16_MAX);
return handle++;
}
static void attr_table_entry_encode_header(void *entry, size_t length,
uint8_t attr_type,
uint16_t string_handle)
{
struct pldm_bios_attr_table_entry *attr_entry = entry;
assert(sizeof(*attr_entry) <= length);
attr_entry->attr_handle = htole16(get_bios_attr_handle());
attr_entry->attr_type = attr_type;
attr_entry->string_handle = htole16(string_handle);
}
size_t pldm_bios_table_attr_entry_enum_encode_length(uint8_t pv_num,
uint8_t def_num)
{
return sizeof(struct pldm_bios_attr_table_entry) -
MEMBER_SIZE(pldm_bios_attr_table_entry, metadata) +
sizeof(pv_num) + pv_num * sizeof(uint16_t) + sizeof(def_num) +
def_num;
}
void pldm_bios_table_attr_entry_enum_encode(
void *entry, size_t entry_length,
const struct pldm_bios_table_attr_entry_enum_info *info)
{
size_t length = pldm_bios_table_attr_entry_enum_encode_length(
info->pv_num, info->def_num);
assert(length <= entry_length);
uint8_t attr_type = info->read_only ? PLDM_BIOS_ENUMERATION_READ_ONLY
: PLDM_BIOS_ENUMERATION;
attr_table_entry_encode_header(entry, entry_length, attr_type,
info->name_handle);
struct pldm_bios_attr_table_entry *attr_entry = entry;
attr_entry->metadata[0] = info->pv_num;
uint16_t *pv_hdls =
(uint16_t *)(attr_entry->metadata + 1 /* sizeof(pv num) */);
size_t i;
for (i = 0; i < info->pv_num; i++)
pv_hdls[i] = htole16(info->pv_handle[i]);
attr_entry->metadata[1 + info->pv_num * sizeof(uint16_t)] =
info->def_num;
memcpy(attr_entry->metadata + 1 /* sizeof(pv num) */ +
info->pv_num * sizeof(uint16_t) + 1 /* sizeof(def num)*/,
info->def_index, info->def_num);
}
int pldm_bios_table_attr_entry_enum_encode_check(
void *entry, size_t entry_length,
const struct pldm_bios_table_attr_entry_enum_info *info)
{
POINTER_CHECK(entry);
POINTER_CHECK(info);
size_t length = pldm_bios_table_attr_entry_enum_encode_length(
info->pv_num, info->def_num);
BUFFER_SIZE_EXPECT(entry_length, length);
pldm_bios_table_attr_entry_enum_encode(entry, entry_length, info);
return PLDM_SUCCESS;
}
#define ATTR_TYPE_EXPECT(type, expected) \
do { \
if (type != expected && type != (expected | 0x80)) \
return PLDM_ERROR_INVALID_DATA; \
} while (0)
uint8_t pldm_bios_table_attr_entry_enum_decode_pv_num(
const struct pldm_bios_attr_table_entry *entry)
{
return entry->metadata[0];
}
int pldm_bios_table_attr_entry_enum_decode_pv_num_check(
const struct pldm_bios_attr_table_entry *entry, uint8_t *pv_num)
{
POINTER_CHECK(entry);
POINTER_CHECK(pv_num);
ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_ENUMERATION);
*pv_num = pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
return PLDM_SUCCESS;
}
uint8_t pldm_bios_table_attr_entry_enum_decode_def_num(
const struct pldm_bios_attr_table_entry *entry)
{
uint8_t pv_num = pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
return entry->metadata[sizeof(uint8_t) /* pv_num */ +
sizeof(uint16_t) * pv_num];
}
int pldm_bios_table_attr_entry_enum_decode_def_num_check(
const struct pldm_bios_attr_table_entry *entry, uint8_t *def_num)
{
POINTER_CHECK(entry);
POINTER_CHECK(def_num);
ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_ENUMERATION);
*def_num = pldm_bios_table_attr_entry_enum_decode_def_num(entry);
return PLDM_SUCCESS;
}
uint8_t pldm_bios_table_attr_entry_enum_decode_pv_hdls(
const struct pldm_bios_attr_table_entry *entry, uint16_t *pv_hdls,
uint8_t pv_num)
{
uint8_t num = pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
num = num < pv_num ? num : pv_num;
size_t i;
for (i = 0; i < num; i++) {
uint16_t *hdl = (uint16_t *)(entry->metadata + sizeof(uint8_t) +
i * sizeof(uint16_t));
pv_hdls[i] = le16toh(*hdl);
}
return num;
}
int pldm_bios_table_attr_entry_enum_decode_pv_hdls_check(
const struct pldm_bios_attr_table_entry *entry, uint16_t *pv_hdls,
uint8_t pv_num)
{
POINTER_CHECK(entry);
POINTER_CHECK(pv_hdls);
ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_ENUMERATION);
uint8_t num = pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
if (num != pv_num)
return PLDM_ERROR_INVALID_DATA;
pldm_bios_table_attr_entry_enum_decode_pv_hdls(entry, pv_hdls, pv_num);
return PLDM_SUCCESS;
}
/** @brief Get length of an enum attribute entry
*/
static size_t
attr_table_entry_length_enum(const struct pldm_bios_attr_table_entry *entry)
{
uint8_t pv_num = pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
uint8_t def_num = pldm_bios_table_attr_entry_enum_decode_def_num(entry);
return pldm_bios_table_attr_entry_enum_encode_length(pv_num, def_num);
}
struct attr_table_string_entry_fields {
uint8_t string_type;
uint16_t min_length;
uint16_t max_length;
uint16_t def_length;
uint8_t def_string[1];
} __attribute__((packed));
size_t pldm_bios_table_attr_entry_string_encode_length(uint16_t def_str_len)
{
return sizeof(struct pldm_bios_attr_table_entry) -
MEMBER_SIZE(pldm_bios_attr_table_entry, metadata) +
sizeof(struct attr_table_string_entry_fields) -
MEMBER_SIZE(attr_table_string_entry_fields, def_string) +
def_str_len;
}
void pldm_bios_table_attr_entry_string_encode(
void *entry, size_t entry_length,
const struct pldm_bios_table_attr_entry_string_info *info)
{
size_t length =
pldm_bios_table_attr_entry_string_encode_length(info->def_length);
assert(length <= entry_length);
uint8_t attr_type =
info->read_only ? PLDM_BIOS_STRING_READ_ONLY : PLDM_BIOS_STRING;
attr_table_entry_encode_header(entry, entry_length, attr_type,
info->name_handle);
struct pldm_bios_attr_table_entry *attr_entry = entry;
struct attr_table_string_entry_fields *attr_fields =
(struct attr_table_string_entry_fields *)attr_entry->metadata;
attr_fields->string_type = info->string_type;
attr_fields->min_length = htole16(info->min_length);
attr_fields->max_length = htole16(info->max_length);
attr_fields->def_length = htole16(info->def_length);
if (info->def_length != 0 && info->def_string != NULL)
memcpy(attr_fields->def_string, info->def_string,
info->def_length);
}
int pldm_bios_table_attr_entry_string_encode_check(
void *entry, size_t entry_length,
const struct pldm_bios_table_attr_entry_string_info *info)
{
POINTER_CHECK(entry);
POINTER_CHECK(info);
size_t length =
pldm_bios_table_attr_entry_string_encode_length(info->def_length);
BUFFER_SIZE_EXPECT(entry_length, length);
if (info->def_length > info->max_length ||
info->def_length < info->min_length ||
info->min_length > info->max_length)
return PLDM_ERROR_INVALID_DATA;
if (info->string_type > 5 && info->string_type != 0xFF)
return PLDM_ERROR_INVALID_DATA;
pldm_bios_table_attr_entry_string_encode(entry, entry_length, info);
return PLDM_SUCCESS;
}
uint16_t pldm_bios_table_attr_entry_string_decode_def_string_length(
const struct pldm_bios_attr_table_entry *entry)
{
struct attr_table_string_entry_fields *fields =
(struct attr_table_string_entry_fields *)entry->metadata;
return le16toh(fields->def_length);
}
int pldm_bios_table_attr_entry_string_decode_def_string_length_check(
const struct pldm_bios_attr_table_entry *entry, uint16_t *def_string_length)
{
POINTER_CHECK(entry);
POINTER_CHECK(def_string_length);
ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_STRING);
*def_string_length =
pldm_bios_table_attr_entry_string_decode_def_string_length(entry);
return PLDM_SUCCESS;
}
/** @brief Get length of a string attribute entry
*/
static size_t
attr_table_entry_length_string(const struct pldm_bios_attr_table_entry *entry)
{
uint16_t def_str_len =
pldm_bios_table_attr_entry_string_decode_def_string_length(entry);
return pldm_bios_table_attr_entry_string_encode_length(def_str_len);
}
struct attr_table_entry {
uint8_t attr_type;
size_t (*entry_length_handler)(
const struct pldm_bios_attr_table_entry *);
};
static struct attr_table_entry attr_table_entrys[] = {
{.attr_type = PLDM_BIOS_ENUMERATION,
.entry_length_handler = attr_table_entry_length_enum},
{.attr_type = PLDM_BIOS_ENUMERATION_READ_ONLY,
.entry_length_handler = attr_table_entry_length_enum},
{.attr_type = PLDM_BIOS_STRING,
.entry_length_handler = attr_table_entry_length_string},
{.attr_type = PLDM_BIOS_STRING_READ_ONLY,
.entry_length_handler = attr_table_entry_length_string},
};
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
static struct attr_table_entry *find_attr_table_entry_by_type(uint8_t attr_type)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(attr_table_entrys); i++) {
if (attr_type == attr_table_entrys[i].attr_type)
return &attr_table_entrys[i];
}
return NULL;
}
static size_t attr_table_entry_length(const void *table_entry)
{
const struct pldm_bios_attr_table_entry *entry = table_entry;
struct attr_table_entry *attr_table_entry =
find_attr_table_entry_by_type(entry->attr_type);
assert(attr_table_entry != NULL);
assert(attr_table_entry->entry_length_handler != NULL);
return attr_table_entry->entry_length_handler(entry);
}
size_t pldm_bios_table_attr_value_entry_encode_enum_length(uint8_t count)
{
return sizeof(struct pldm_bios_attr_val_table_entry) - 1 +
sizeof(count) + count;
}
void pldm_bios_table_attr_value_entry_encode_enum(
void *entry, size_t entry_length, uint16_t attr_handle, uint8_t attr_type,
uint8_t count, uint8_t *handles)
{
size_t length =
pldm_bios_table_attr_value_entry_encode_enum_length(count);
assert(length <= entry_length);
struct pldm_bios_attr_val_table_entry *table_entry = entry;
table_entry->attr_handle = htole16(attr_handle);
table_entry->attr_type = attr_type;
table_entry->value[0] = count;
if (count != 0)
memcpy(&table_entry->value[1], handles, count);
}
int pldm_bios_table_attr_value_entry_encode_enum_check(
void *entry, size_t entry_length, uint16_t attr_handle, uint8_t attr_type,
uint8_t count, uint8_t *handles)
{
POINTER_CHECK(entry);
if (count != 0 && handles == NULL)
return PLDM_ERROR_INVALID_DATA;
ATTR_TYPE_EXPECT(attr_type, PLDM_BIOS_ENUMERATION);
size_t length =
pldm_bios_table_attr_value_entry_encode_enum_length(count);
BUFFER_SIZE_EXPECT(entry_length, length);
pldm_bios_table_attr_value_entry_encode_enum(
entry, entry_length, attr_handle, attr_type, count, handles);
return PLDM_SUCCESS;
}
size_t
pldm_bios_table_attr_value_entry_encode_string_length(uint16_t string_length)
{
return sizeof(struct pldm_bios_attr_val_table_entry) - 1 +
sizeof(string_length) + string_length;
}
void pldm_bios_table_attr_value_entry_encode_string(
void *entry, size_t entry_length, uint16_t attr_handle, uint8_t attr_type,
uint16_t str_length, const char *str)
{
size_t length =
pldm_bios_table_attr_value_entry_encode_string_length(str_length);
assert(length <= entry_length);
struct pldm_bios_attr_val_table_entry *table_entry = entry;
table_entry->attr_handle = htole16(attr_handle);
table_entry->attr_type = attr_type;
if (str_length != 0)
memcpy(table_entry->value + sizeof(str_length), str,
str_length);
str_length = htole16(str_length);
memcpy(table_entry->value, &str_length, sizeof(str_length));
}
int pldm_bios_table_attr_value_entry_encode_string_check(
void *entry, size_t entry_length, uint16_t attr_handle, uint8_t attr_type,
uint16_t str_length, const char *str)
{
POINTER_CHECK(entry);
if (str_length != 0 && str == NULL)
return PLDM_ERROR_INVALID_DATA;
ATTR_TYPE_EXPECT(attr_type, PLDM_BIOS_STRING);
size_t length =
pldm_bios_table_attr_value_entry_encode_string_length(str_length);
BUFFER_SIZE_EXPECT(entry_length, length);
pldm_bios_table_attr_value_entry_encode_string(
entry, entry_length, attr_handle, attr_type, str_length, str);
return PLDM_SUCCESS;
}
struct pldm_bios_table_iter {
const uint8_t *table_data;
size_t table_len;
size_t current_pos;
size_t (*entry_length_handler)(const void *table_entry);
};
struct pldm_bios_table_iter *
pldm_bios_table_iter_create(const void *table, size_t length,
enum pldm_bios_table_types type)
{
struct pldm_bios_table_iter *iter = malloc(sizeof(*iter));
assert(iter != NULL);
iter->table_data = table;
iter->table_len = length;
iter->current_pos = 0;
iter->entry_length_handler = NULL;
switch (type) {
case PLDM_BIOS_STRING_TABLE:
iter->entry_length_handler = string_table_entry_length;
break;
case PLDM_BIOS_ATTR_TABLE:
iter->entry_length_handler = attr_table_entry_length;
break;
case PLDM_BIOS_ATTR_VAL_TABLE:
break;
}
return iter;
}
void pldm_bios_table_iter_free(struct pldm_bios_table_iter *iter)
{
free(iter);
}
#define pad_and_check_max 7
bool pldm_bios_table_iter_is_end(const struct pldm_bios_table_iter *iter)
{
if (iter->table_len - iter->current_pos <= pad_and_check_max)
return true;
return false;
}
void pldm_bios_table_iter_next(struct pldm_bios_table_iter *iter)
{
if (pldm_bios_table_iter_is_end(iter))
return;
const void *entry = iter->table_data + iter->current_pos;
iter->current_pos += iter->entry_length_handler(entry);
}
const void *pldm_bios_table_iter_value(struct pldm_bios_table_iter *iter)
{
return iter->table_data + iter->current_pos;
}
static const void *
pldm_bios_table_entry_find(struct pldm_bios_table_iter *iter, const void *key,
int (*equal)(const void *entry, const void *key))
{
const void *entry;
while (!pldm_bios_table_iter_is_end(iter)) {
entry = pldm_bios_table_iter_value(iter);
if (equal(entry, key))
return entry;
pldm_bios_table_iter_next(iter);
}
return NULL;
}
static int string_table_handle_equal(const void *entry, const void *key)
{
const struct pldm_bios_string_table_entry *string_entry = entry;
uint16_t handle = *(uint16_t *)key;
if (pldm_bios_table_string_entry_decode_handle(string_entry) == handle)
return true;
return false;
}
struct string_equal_arg {
uint16_t str_length;
const char *str;
};
static int string_table_string_equal(const void *entry, const void *key)
{
const struct pldm_bios_string_table_entry *string_entry = entry;
const struct string_equal_arg *arg = key;
if (arg->str_length !=
pldm_bios_table_string_entry_decode_string_length(string_entry))
return false;
if (memcmp(string_entry->name, arg->str, arg->str_length) != 0)
return false;
return true;
}
const struct pldm_bios_string_table_entry *
pldm_bios_table_string_find_by_string(const void *table, size_t length,
const char *str)
{
uint16_t str_length = strlen(str);
struct string_equal_arg arg = {str_length, str};
struct pldm_bios_table_iter *iter =
pldm_bios_table_iter_create(table, length, PLDM_BIOS_STRING_TABLE);
const void *entry =
pldm_bios_table_entry_find(iter, &arg, string_table_string_equal);
pldm_bios_table_iter_free(iter);
return entry;
}
const struct pldm_bios_string_table_entry *
pldm_bios_table_string_find_by_handle(const void *table, size_t length,
uint16_t handle)
{
struct pldm_bios_table_iter *iter =
pldm_bios_table_iter_create(table, length, PLDM_BIOS_STRING_TABLE);
const void *entry = pldm_bios_table_entry_find(
iter, &handle, string_table_handle_equal);
pldm_bios_table_iter_free(iter);
return entry;
}