coreboot-kgpe-d16/tests/lib/coreboot_table-test.c
Julius Werner 9a9b2778a1 coreboot_tables: Make existing alignment conventions more explicit
There seem to be some recurring vague concerns about the alignment of
coreboot table entries. While the existing implementation has been
producing tables with a well-defined alignment (4 bytes) for a long
time, the code doesn't always make it very clear. This patch adds an
explicit constant to codify that alignment, assertions to check it after
each entry, and adds explicit padding to the few entry structures that
were relying on compiler padding to return a correct sizeof() value.

Signed-off-by: Julius Werner <jwerner@chromium.org>
Change-Id: Iaeef29ef255047a855066469e03b5481812e5975
Reviewed-on: https://review.coreboot.org/c/coreboot/+/70158
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-by: Jakub Czapiga <jacz@semihalf.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Peter Stuge <peter@stuge.se>
2022-12-22 15:34:28 +00:00

486 lines
15 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
#include <tests/test.h>
#include <boardid.h>
#include <boot/coreboot_tables.h>
#include <boot/tables.h>
#include <cbfs.h>
#include <cbmem.h>
#include <commonlib/helpers.h>
#include <commonlib/region.h>
#include <fmap_config.h>
#include <fw_config.h>
#include <stdbool.h>
#include <version.h>
/* Copy of lb_table_init() implementation for testing purposes */
static struct lb_header *lb_table_init(unsigned long addr)
{
struct lb_header *header;
/* 16 byte align the address */
addr = ALIGN_UP(addr, 16);
header = (void *)addr;
header->signature[0] = 'L';
header->signature[1] = 'B';
header->signature[2] = 'I';
header->signature[3] = 'O';
header->header_bytes = sizeof(*header);
header->header_checksum = 0;
header->table_bytes = 0;
header->table_checksum = 0;
header->table_entries = 0;
return header;
}
static struct lb_record *lb_first_record(struct lb_header *header)
{
struct lb_record *rec;
rec = (void *)(((char *)header) + sizeof(*header));
return rec;
}
#define LB_RECORD_FOR_EACH(record_ptr, index, header) \
for (index = 0, record_ptr = lb_first_record(header); index < header->table_entries; \
record_ptr = (struct lb_record *)((uintptr_t)record_ptr + record_ptr->size), \
index++)
static void test_lb_add_gpios(void **state)
{
struct lb_gpio gpios[] = {
{-1, ACTIVE_HIGH, 1, "lid"},
{-1, ACTIVE_HIGH, 0, "power"},
{-1, ACTIVE_HIGH, 1, "oprom"},
{-1, ACTIVE_HIGH, 0, "EC in RW"},
};
const size_t gpios_buf_size = sizeof(struct lb_gpios) + sizeof(struct lb_gpio) * 32;
uint8_t gpios_buf[gpios_buf_size];
struct lb_gpios *gpios_table = (struct lb_gpios *)gpios_buf;
gpios_table->count = 0;
gpios_table->size = 0;
gpios_table->tag = LB_TAG_GPIO;
/* Add GPIOs an check if they have been added to the table.
GPIOs are added in the same order to the end of the table. */
lb_add_gpios(gpios_table, gpios, ARRAY_SIZE(gpios));
assert_int_equal(ARRAY_SIZE(gpios), gpios_table->count);
assert_int_equal(sizeof(gpios), gpios_table->size);
assert_memory_equal(&gpios_table->gpios[0], gpios, sizeof(gpios));
/* Add subset of gpios and check if they have been added correctly. */
lb_add_gpios(gpios_table, &gpios[1], 2);
assert_int_equal(ARRAY_SIZE(gpios) + 2, gpios_table->count);
assert_int_equal(sizeof(gpios) + 2 * sizeof(gpios[0]), gpios_table->size);
assert_memory_equal(&gpios_table->gpios[0], gpios, sizeof(gpios));
assert_memory_equal(&gpios_table->gpios[ARRAY_SIZE(gpios)], &gpios[1],
2 * sizeof(gpios[0]));
}
uint8_t tables_buffer[sizeof(struct lb_header) + 10 * KiB];
static int setup_test_header(void **state)
{
*state = lb_table_init((uintptr_t)tables_buffer);
return 0;
}
static void test_lb_new_record(void **state)
{
struct lb_header *header = *state;
const size_t entries = 10;
int i;
size_t entries_offset;
size_t accumulated_size = 0;
struct lb_record *curr;
assert_int_equal(0, header->table_entries);
assert_int_equal(0, header->table_bytes);
lb_new_record(header);
assert_int_equal(1, header->table_entries);
assert_int_equal(0, header->table_bytes);
/* Create few entries with varying sizes (but at least of sizeof(struct lb_record))
Accumulate and check size of table after each lb_new_record() call. */
entries_offset = header->table_entries;
accumulated_size = sizeof(struct lb_record);
for (i = 0; i < entries; ++i) {
curr = lb_new_record(header);
curr->size = sizeof(struct lb_record) +
ALIGN_UP(((i + 2) * 7) % 32, LB_ENTRY_ALIGN);
assert_int_equal(entries_offset + (i + 1), header->table_entries);
assert_int_equal(accumulated_size, header->table_bytes);
accumulated_size += curr->size;
}
}
static void test_lb_add_console(void **state)
{
struct lb_header *header = *state;
lb_add_console(LB_TAG_CONSOLE_SERIAL8250MEM, header);
assert_int_equal(1, header->table_entries);
/* Table bytes and checksum should be zero, because it is updated with size of previous
record or when table is closed. No previous record is present. */
assert_int_equal(0, header->table_bytes);
assert_int_equal(0, header->table_checksum);
}
static void test_multiple_entries(void **state)
{
struct lb_header *header = *state;
/* Add two entries */
lb_add_console(LB_TAG_CONSOLE_SERIAL8250, header);
lb_add_console(LB_TAG_CONSOLE_SERIAL8250MEM, header);
assert_int_equal(2, header->table_entries);
assert_int_equal(sizeof(struct lb_console), header->table_bytes);
}
static void test_write_coreboot_forwarding_table(void **state)
{
struct lb_header *header = *state;
uint8_t forwarding_table_buffer[sizeof(struct lb_header)
+ 2 * sizeof(struct lb_forward)];
struct lb_header *forward_header =
(struct lb_header *)ALIGN_UP((uintptr_t)forwarding_table_buffer, 16);
size_t forwarding_table_size = write_coreboot_forwarding_table(
(uintptr_t)forwarding_table_buffer, (uintptr_t)header);
size_t expected_forwarding_table_size =
ALIGN_UP((uintptr_t)forwarding_table_buffer, 16) + sizeof(struct lb_header)
+ sizeof(struct lb_forward) - (uintptr_t)forwarding_table_buffer;
assert_int_equal(expected_forwarding_table_size, forwarding_table_size);
assert_int_equal(1, forward_header->table_entries);
assert_int_equal(sizeof(struct lb_forward), forward_header->table_bytes);
assert_ptr_equal(header,
((struct lb_forward *)lb_first_record(forward_header))->forward);
}
/* Mocks for write_tables() */
const char mainboard_vendor[] = CONFIG_MAINBOARD_VENDOR;
const char mainboard_part_number[] = CONFIG_MAINBOARD_PART_NUMBER;
const char coreboot_version[] = "4.13";
const char coreboot_extra_version[] = "abcdef";
const char coreboot_build[] = "Coreboot build info";
const unsigned int coreboot_version_timestamp = 1617191902U;
const unsigned int coreboot_major_revision = 4;
const unsigned int coreboot_minor_revision = 13;
const char coreboot_compile_time[] = "13:58:22";
const char coreboot_dmi_date[] = "03/31/2021";
const struct bcd_date coreboot_build_date = {
.century = 0x20,
.year = 0x20,
.month = 0x03,
.day = 0x31,
.weekday = 0x2,
};
const unsigned int asl_revision = 0x20200925;
void arch_write_tables(uintptr_t coreboot_table)
{
}
static const uintptr_t ebda_base = 0xf0000;
uintptr_t get_coreboot_rsdp(void)
{
return ebda_base;
}
struct resource mock_bootmem_ranges[] = {
{.base = 0x1000, .size = 0x2000, .flags = LB_MEM_RAM},
{.base = 0x0000, .size = 0x4000, .flags = LB_MEM_RAM},
};
void bootmem_write_memory_table(struct lb_memory *mem)
{
struct lb_memory_range *lb_r = &mem->map[0];
int i;
/* Insert entries for testing */
for (i = 0; i < ARRAY_SIZE(mock_bootmem_ranges); ++i) {
struct resource *res = &mock_bootmem_ranges[i];
lb_r->start = res->base;
lb_r->size = res->size;
lb_r->type = res->flags;
lb_r++;
mem->size += sizeof(struct lb_memory_range);
}
}
enum cb_err fill_lb_serial(struct lb_serial *serial)
{
serial->type = LB_SERIAL_TYPE_MEMORY_MAPPED;
serial->baseaddr = 0xFEDC6000;
serial->baud = 115200;
serial->regwidth = 1;
serial->input_hertz = 115200 * 16;
return CB_SUCCESS;
}
struct cbfs_boot_device cbfs_boot_dev = {
.rdev = REGION_DEV_INIT(NULL, 0, 0x1000),
.mcache = (void *)0x1000,
.mcache_size = 0x1000,
};
const struct cbfs_boot_device *cbfs_get_boot_device(bool force_ro)
{
return &cbfs_boot_dev;
}
void cbmem_run_init_hooks(int is_recovery)
{
}
extern uintptr_t _cbmem_top_ptr;
uintptr_t cbmem_top_chipset(void)
{
return _cbmem_top_ptr;
}
#define CBMEM_SIZE (64 * KiB)
static int teardown_write_tables_test(void **state)
{
free(*state);
_cbmem_top_ptr = 0;
return 0;
}
static int setup_write_tables_test(void **state)
{
/* Allocate more data to have space for alignment */
void *top_ptr = malloc(CBMEM_SIZE + DYN_CBMEM_ALIGN_SIZE);
int32_t *mmc_status = NULL;
if (!top_ptr)
return -1;
*state = top_ptr;
_cbmem_top_ptr = ALIGN_UP((uintptr_t)top_ptr + CBMEM_SIZE, DYN_CBMEM_ALIGN_SIZE);
cbmem_initialize_empty();
mmc_status = cbmem_add(CBMEM_ID_MMC_STATUS, sizeof(int32_t));
if (mmc_status == NULL) {
teardown_write_tables_test(state);
return -1;
}
*mmc_status = 0x4433AADD;
return 0;
}
const struct region_device *boot_device_ro(void)
{
return &cbfs_boot_dev.rdev;
}
uint64_t get_fmap_flash_offset(void)
{
return FMAP_OFFSET;
}
uint32_t freq_khz = 5000 * 1000;
void lb_arch_add_records(struct lb_header *header)
{
struct lb_tsc_info *tsc_info;
tsc_info = (void *)lb_new_record(header);
tsc_info->tag = LB_TAG_TSC_INFO;
tsc_info->size = sizeof(*tsc_info);
tsc_info->freq_khz = freq_khz;
}
static void test_write_tables(void **state)
{
void *cbtable_start;
struct lb_header *header;
struct lb_record *record;
int32_t *mmc_status = cbmem_find(CBMEM_ID_MMC_STATUS);
size_t i = 0;
/* Expect function to store cbtable entry in cbmem */
cbtable_start = write_tables();
assert_ptr_equal(cbtable_start, cbmem_find(CBMEM_ID_CBTABLE));
/* Expect correct lb_header at cbtable_start address */
header = (struct lb_header *)cbtable_start;
assert_non_null(header);
assert_memory_equal("LBIO", header, 4);
assert_int_equal(sizeof(*header), header->header_bytes);
/* At least one entry should be present. */
assert_int_not_equal(0, header->table_entries);
LB_RECORD_FOR_EACH(record, i, header)
{
switch (record->tag) {
case LB_TAG_MEMORY:
/* Should be the same as in bootmem_write_memory_table() */
assert_int_equal(sizeof(struct lb_memory)
+ ARRAY_SIZE(mock_bootmem_ranges)
* sizeof(struct lb_memory_range),
record->size);
const struct lb_memory *memory = (struct lb_memory *)record;
const struct lb_memory_range *range;
const struct resource *res;
lb_uint64_t value;
for (int i = 0; i < ARRAY_SIZE(mock_bootmem_ranges); ++i) {
res = &mock_bootmem_ranges[i];
range = &memory->map[i];
value = res->base;
assert_memory_equal(&value, &range->start,
sizeof(lb_uint64_t));
value = res->size;
assert_memory_equal(&value, &range->size,
sizeof(lb_uint64_t));
assert_int_equal(range->type, res->flags);
}
break;
case LB_TAG_MAINBOARD:
/* Mainboard record contains its header followed
by two null-terminated strings */
assert_int_equal(ALIGN_UP(sizeof(struct lb_mainboard)
+ ARRAY_SIZE(mainboard_vendor)
+ ARRAY_SIZE(mainboard_part_number),
LB_ENTRY_ALIGN),
record->size);
break;
case LB_TAG_VERSION:
assert_int_equal(ALIGN_UP(sizeof(struct lb_string)
+ ARRAY_SIZE(coreboot_version),
LB_ENTRY_ALIGN),
record->size);
break;
case LB_TAG_EXTRA_VERSION:
assert_int_equal(ALIGN_UP(sizeof(struct lb_string)
+ ARRAY_SIZE(coreboot_extra_version),
LB_ENTRY_ALIGN),
record->size);
break;
case LB_TAG_BUILD:
assert_int_equal(
ALIGN_UP(sizeof(struct lb_string) + ARRAY_SIZE(coreboot_build),
LB_ENTRY_ALIGN),
record->size);
break;
case LB_TAG_COMPILE_TIME:
assert_int_equal(ALIGN_UP(sizeof(struct lb_string)
+ ARRAY_SIZE(coreboot_compile_time),
LB_ENTRY_ALIGN),
record->size);
break;
case LB_TAG_SERIAL:
assert_int_equal(sizeof(struct lb_serial), record->size);
/* This struct have the same values as created in uart_fill_lb() */
const struct lb_serial *serial = (struct lb_serial *)record;
assert_int_equal(LB_SERIAL_TYPE_MEMORY_MAPPED, serial->type);
assert_int_equal(0xFEDC6000, serial->baseaddr);
assert_int_equal(115200, serial->baud);
assert_int_equal(1, serial->regwidth);
assert_int_equal(115200 * 16, serial->input_hertz);
break;
case LB_TAG_CONSOLE:
assert_int_equal(sizeof(struct lb_console), record->size);
/* This struct have the same values as created in uart_fill_lb() */
const struct lb_console *console = (struct lb_console *)record;
assert_int_equal(LB_TAG_CONSOLE_SERIAL8250MEM, console->type);
break;
case LB_TAG_VERSION_TIMESTAMP:
assert_int_equal(sizeof(struct lb_timestamp), record->size);
const struct lb_timestamp *timestamp = (struct lb_timestamp *)record;
assert_int_equal(coreboot_version_timestamp, timestamp->timestamp);
break;
case LB_TAG_BOOT_MEDIA_PARAMS:
assert_int_equal(sizeof(struct lb_boot_media_params), record->size);
const struct lb_boot_media_params *bmp =
(struct lb_boot_media_params *)record;
const struct cbfs_boot_device *cbd = cbfs_get_boot_device(false);
const struct region_device *boot_dev = boot_device_ro();
assert_int_equal(region_device_offset(&cbd->rdev), bmp->cbfs_offset);
assert_int_equal(region_device_sz(&cbd->rdev), bmp->cbfs_size);
assert_int_equal(region_device_sz(boot_dev), bmp->boot_media_size);
assert_int_equal(get_fmap_flash_offset(), bmp->fmap_offset);
break;
case LB_TAG_CBMEM_ENTRY:
assert_int_equal(sizeof(struct lb_cbmem_entry), record->size);
const struct lb_cbmem_entry *cbmem_entry =
(struct lb_cbmem_entry *)record;
const LargestIntegralType expected_tags[] = {CBMEM_ID_CBTABLE,
CBMEM_ID_MMC_STATUS};
assert_in_set(cbmem_entry->id, expected_tags,
ARRAY_SIZE(expected_tags));
break;
case LB_TAG_TSC_INFO:
assert_int_equal(sizeof(struct lb_tsc_info), record->size);
const struct lb_tsc_info *tsc_info = (struct lb_tsc_info *)record;
assert_int_equal(freq_khz, tsc_info->freq_khz);
break;
case LB_TAG_MMC_INFO:
assert_int_equal(sizeof(struct lb_mmc_info), record->size);
const struct lb_mmc_info *mmc_info = (struct lb_mmc_info *)record;
assert_int_equal(*mmc_status, mmc_info->early_cmd1_status);
break;
case LB_TAG_BOARD_CONFIG:
assert_int_equal(sizeof(struct lb_board_config), record->size);
const struct lb_board_config *board_config =
(struct lb_board_config *)record;
const lb_uint64_t expected_fw_version = fw_config_get();
assert_memory_equal(&expected_fw_version, &board_config->fw_config,
sizeof(lb_uint64_t));
assert_int_equal(board_id(), board_config->board_id);
assert_int_equal(ram_code(), board_config->ram_code);
assert_int_equal(sku_id(), board_config->sku_id);
break;
case LB_TAG_ACPI_RSDP:
assert_int_equal(sizeof(struct lb_acpi_rsdp), record->size);
const struct lb_acpi_rsdp *acpi_rsdp = (struct lb_acpi_rsdp *)record;
assert_int_equal(ebda_base, acpi_rsdp->rsdp_pointer);
break;
default:
fail_msg("Unexpected tag found in record. Tag ID: 0x%x", record->tag);
}
}
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_lb_add_gpios),
cmocka_unit_test_setup(test_lb_new_record, setup_test_header),
cmocka_unit_test_setup(test_lb_add_console, setup_test_header),
cmocka_unit_test_setup(test_multiple_entries, setup_test_header),
cmocka_unit_test_setup(test_write_coreboot_forwarding_table, setup_test_header),
cmocka_unit_test_setup_teardown(test_write_tables, setup_write_tables_test,
teardown_write_tables_test),
};
return cb_run_group_tests(tests, NULL, NULL);
}