| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * efi_selftest_config_tables |
| * |
| * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> |
| * |
| * This test checks the following service: |
| * InstallConfigurationTable. |
| */ |
| |
| #include <efi_selftest.h> |
| |
| static const struct efi_system_table *sys_table; |
| static struct efi_boot_services *boottime; |
| |
| static efi_guid_t table_guid = |
| EFI_GUID(0xff1c3f9e, 0x795b, 0x1529, 0xf1, 0x55, |
| 0x17, 0x2e, 0x51, 0x6b, 0x49, 0x75); |
| |
| /* |
| * Notification function, increments the notification count if parameter |
| * context is provided. |
| * |
| * @event notified event |
| * @context pointer to the notification count |
| */ |
| static void EFIAPI notify(struct efi_event *event, void *context) |
| { |
| unsigned int *count = context; |
| |
| if (count) |
| ++*count; |
| } |
| |
| /* |
| * Check CRC32 of a table. |
| */ |
| static int check_table(const void *table) |
| { |
| efi_status_t ret; |
| u32 crc32, res; |
| /* Casting from constant to not constant */ |
| struct efi_table_hdr *hdr = (struct efi_table_hdr *)table; |
| |
| crc32 = hdr->crc32; |
| /* |
| * Setting the CRC32 of the 'const' table to zero is easier than |
| * copying |
| */ |
| hdr->crc32 = 0; |
| ret = boottime->calculate_crc32(table, hdr->headersize, &res); |
| /* Reset table CRC32 so it stays constant */ |
| hdr->crc32 = crc32; |
| if (ret != EFI_ST_SUCCESS) { |
| efi_st_error("CalculateCrc32 failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (res != crc32) { |
| efi_st_error("Incorrect CRC32\n"); |
| return EFI_ST_FAILURE; |
| } |
| return EFI_ST_SUCCESS; |
| } |
| |
| /* |
| * Setup unit test. |
| * |
| * @handle: handle of the loaded image |
| * @systable: system table |
| * @return: EFI_ST_SUCCESS for success |
| */ |
| static int setup(const efi_handle_t handle, |
| const struct efi_system_table *systable) |
| { |
| sys_table = systable; |
| boottime = systable->boottime; |
| |
| return EFI_ST_SUCCESS; |
| } |
| |
| /* |
| * Execute unit test. |
| * |
| * A table is installed, updated, removed. The table entry and the |
| * triggering of events is checked. |
| * |
| * @return: EFI_ST_SUCCESS for success |
| */ |
| static int execute(void) |
| { |
| efi_status_t ret; |
| unsigned int counter = 0; |
| struct efi_event *event; |
| void *table; |
| const unsigned int tables[2]; |
| efi_uintn_t i; |
| efi_uintn_t tabcnt; |
| efi_uintn_t table_count = sys_table->nr_tables; |
| |
| ret = boottime->create_event_ex(0, TPL_NOTIFY, |
| notify, (void *)&counter, |
| &table_guid, &event); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Failed to create event\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| /* Try to delete non-existent table */ |
| ret = boottime->install_configuration_table(&table_guid, NULL); |
| if (ret != EFI_NOT_FOUND) { |
| efi_st_error("Failed to detect missing table\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (counter) { |
| efi_st_error("Notification function was called.\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Check if the event was signaled */ |
| ret = boottime->check_event(event); |
| if (ret == EFI_SUCCESS) { |
| efi_st_error("Event was signaled on EFI_NOT_FOUND\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (counter != 1) { |
| efi_st_error("Notification function was not called.\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (table_count != sys_table->nr_tables) { |
| efi_st_error("Incorrect table count %u, expected %u\n", |
| (unsigned int)sys_table->nr_tables, |
| (unsigned int)table_count); |
| return EFI_ST_FAILURE; |
| } |
| |
| /* Install table */ |
| ret = boottime->install_configuration_table(&table_guid, |
| (void *)&tables[0]); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Failed to install table\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Check signaled state */ |
| ret = boottime->check_event(event); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Event was not signaled on insert\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (++table_count != sys_table->nr_tables) { |
| efi_st_error("Incorrect table count %u, expected %u\n", |
| (unsigned int)sys_table->nr_tables, |
| (unsigned int)table_count); |
| return EFI_ST_FAILURE; |
| } |
| table = NULL; |
| for (i = 0; i < sys_table->nr_tables; ++i) { |
| if (!memcmp(&sys_table->tables[i].guid, &table_guid, |
| sizeof(efi_guid_t))) |
| table = sys_table->tables[i].table; |
| } |
| if (!table) { |
| efi_st_error("Installed table not found\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (table != &tables[0]) { |
| efi_st_error("Incorrect table address\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (check_table(sys_table) != EFI_ST_SUCCESS) { |
| efi_st_error("Checking system table\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| /* Update table */ |
| ret = boottime->install_configuration_table(&table_guid, |
| (void *)&tables[1]); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Failed to update table\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Check signaled state */ |
| ret = boottime->check_event(event); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Event was not signaled on update\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (table_count != sys_table->nr_tables) { |
| efi_st_error("Incorrect table count %u, expected %u\n", |
| (unsigned int)sys_table->nr_tables, |
| (unsigned int)table_count); |
| return EFI_ST_FAILURE; |
| } |
| table = NULL; |
| tabcnt = 0; |
| for (i = 0; i < sys_table->nr_tables; ++i) { |
| if (!memcmp(&sys_table->tables[i].guid, &table_guid, |
| sizeof(efi_guid_t))) { |
| table = sys_table->tables[i].table; |
| ++tabcnt; |
| } |
| } |
| if (!table) { |
| efi_st_error("Installed table not found\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (tabcnt > 1) { |
| efi_st_error("Duplicate table GUID\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (table != &tables[1]) { |
| efi_st_error("Incorrect table address\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (check_table(sys_table) != EFI_ST_SUCCESS) { |
| efi_st_error("Checking system table\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| /* Delete table */ |
| ret = boottime->install_configuration_table(&table_guid, NULL); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Failed to delete table\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Check signaled state */ |
| ret = boottime->check_event(event); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Event was not signaled on delete\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (--table_count != sys_table->nr_tables) { |
| efi_st_error("Incorrect table count %u, expected %u\n", |
| (unsigned int)sys_table->nr_tables, |
| (unsigned int)table_count); |
| return EFI_ST_FAILURE; |
| } |
| table = NULL; |
| for (i = 0; i < sys_table->nr_tables; ++i) { |
| if (!memcmp(&sys_table->tables[i].guid, &table_guid, |
| sizeof(efi_guid_t))) { |
| table = sys_table->tables[i].table; |
| } |
| } |
| if (table) { |
| efi_st_error("Wrong table deleted\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| ret = boottime->close_event(event); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Failed to close event\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (check_table(sys_table) != EFI_ST_SUCCESS) { |
| efi_st_error("Checking system table\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| return EFI_ST_SUCCESS; |
| } |
| |
| EFI_UNIT_TEST(configtables) = { |
| .name = "configuration tables", |
| .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, |
| .setup = setup, |
| .execute = execute, |
| }; |