// SPDX-License-Identifier: GPL-2.0-only /* * Test ESRT tables support * * Copyright (C) 2021 Arm Ltd. */ #include <common.h> #include <efi_loader.h> #include <efi_selftest.h> // This value must not exceed 255. // An FMP cannot contain more than 255 FW images. #define TEST_ESRT_NUM_ENTRIES 255 static struct efi_firmware_image_descriptor static_img_info[TEST_ESRT_NUM_ENTRIES]; static const struct efi_system_table *local_systable; static efi_handle_t fmp_handle; static const efi_guid_t efi_fmp_guid = EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID; static void efi_test_esrt_init_info(void) { for (int idx = 0; idx < TEST_ESRT_NUM_ENTRIES; idx++) { static_img_info[idx].image_index = idx; // Note: the 16 byte value present in // static_img_info[idx].image_type_id is not strictly a GUID. // The value is used for the sake of code testing. static_img_info[idx].image_type_id.b[0] = idx; static_img_info[idx].image_id = 0; static_img_info[idx].image_id_name = NULL; static_img_info[idx].version = 0; static_img_info[idx].version_name = NULL; static_img_info[idx].size = 0; static_img_info[idx].lowest_supported_image_version = 1; static_img_info[idx].last_attempt_version = 2; static_img_info[idx].last_attempt_status = 3; static_img_info[idx].hardware_instance = 1; } } static efi_status_t EFIAPI efi_test_fmp_get_image_info(struct efi_firmware_management_protocol *this, efi_uintn_t *image_info_size, struct efi_firmware_image_descriptor *image_info, u32 *descriptor_version, u8 *descriptor_count, efi_uintn_t *descriptor_size, u32 *package_version, u16 **package_version_name) { efi_status_t ret = EFI_SUCCESS; if (!image_info_size) return EFI_INVALID_PARAMETER; if (descriptor_version) *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; if (descriptor_count) *descriptor_count = TEST_ESRT_NUM_ENTRIES; if (descriptor_size) *descriptor_size = sizeof(*image_info); if (package_version) *package_version = 0xffffffff; if (package_version_name) *package_version_name = NULL; if (*image_info_size < sizeof(*image_info)) { *image_info_size = *descriptor_size * *descriptor_count; return EFI_BUFFER_TOO_SMALL; } for (int idx = 0; idx < TEST_ESRT_NUM_ENTRIES; idx++) image_info[idx] = static_img_info[idx]; return ret; } static struct efi_firmware_management_protocol efi_test_fmp = { .get_image_info = efi_test_fmp_get_image_info, .get_image = NULL, .set_image = NULL, .check_image = NULL, .get_package_info = NULL, .set_package_info = NULL, }; static void *lib_test_get_esrt(void) { for (int idx = 0; idx < local_systable->nr_tables; idx++) if (!guidcmp(&efi_esrt_guid, &local_systable->tables[idx].guid)) return local_systable->tables[idx].table; return NULL; } /** * lib_test_check_uuid_entry: Find an ESRT entry for which the fw_calss field matches * the image_type_id in the @img_info. * Ensure that all of the field in the ESRT entry have the same value as the corresponding * fields in the @img_info. * * @esrt: pointer to the ESRT * @img_info: an image_info_descriptor output by the FMP get_image_info * * Return: true if matching ESRT entry is found and if all the ESRT entry fields match the * corresponding @img_info fields. */ static bool lib_test_check_uuid_entry(struct efi_system_resource_table *esrt, struct efi_firmware_image_descriptor *img_info) { const u32 filled_entries = esrt->fw_resource_count; struct efi_system_resource_entry *entry = esrt->entries; for (u32 idx = 0; idx < filled_entries; idx++) { if (!guidcmp(&entry[idx].fw_class, &img_info->image_type_id)) { if (entry[idx].fw_version != img_info->version) { efi_st_error("ESRT field mismatch for entry with fw_class=%pU\n", &img_info->image_type_id); return false; } if (entry[idx].lowest_supported_fw_version != img_info->lowest_supported_image_version) { efi_st_error("ESRT field mismatch for entry with fw_class=%pU\n", &img_info->image_type_id); return false; } if (entry[idx].last_attempt_version != img_info->last_attempt_version) { efi_st_error("ESRT field mismatch for entry with fw_class=%pU\n", &img_info->image_type_id); return false; } if (entry[idx].last_attempt_status != img_info->last_attempt_status) { efi_st_error("ESRT field mismatch for entry with fw_class=%pU\n", &img_info->image_type_id); return false; } /* * The entry with fw_class = img_uuid matches with the * remainder fmp input. */ return true; } } /* There exists no entry with fw_class equal to img_uuid in the ESRT. */ efi_st_error("ESRT no entry with fw_class= %pUl\n", &img_info->image_type_id); return false; } /* * Setup unit test. * * Initialize the test FMP datastructure. * * @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) { local_systable = systable; efi_test_esrt_init_info(); return EFI_ST_SUCCESS; } /* * Tear down unit test. * * Uninstall the test FMP. * * Return: EFI_ST_SUCCESS for success */ static int teardown(void) { efi_status_t ret = EFI_SUCCESS; struct efi_boot_services *bt; bt = local_systable->boottime; if (!bt) { efi_st_error("Cannot find boottime services structure\n"); return EFI_ST_FAILURE; } ret = bt->uninstall_multiple_protocol_interfaces (fmp_handle, &efi_fmp_guid, &efi_test_fmp, NULL); if (ret != EFI_SUCCESS) { efi_st_error("Failed to uninstall FMP\n"); return EFI_ST_FAILURE; } return EFI_ST_SUCCESS; } /* * Perform the test * * The test consists of the following steps: * * 1) Obtain the ESRT * 2) Record the number of ESRT entries prior to test start * 3) Install the test FMP * 4) Re-obtain the ESRT (the ESRT pointer may have changed with the FMP install) * 5) verify that the ESRT entries have increased by the number of entries in the * test FMP. * 6) Traverse all the elements used as the test FMP input and verify that each * has a corresponding ESRT entry and that the fields are correctly set. * * The failure of any of the above steps results in a test failure. * */ static int execute(void) { struct efi_system_resource_table *esrt; efi_status_t ret = EFI_SUCCESS; u32 base_entry_count; u32 entry_delta; struct efi_boot_services *bt; bt = local_systable->boottime; if (!bt) { efi_st_error("Cannot find boottime services structure\n"); return EFI_ST_FAILURE; } esrt = lib_test_get_esrt(); if (!esrt) { efi_st_error("ESRT table not present\n"); return EFI_ST_FAILURE; } base_entry_count = esrt->fw_resource_count; ret = bt->install_multiple_protocol_interfaces(&fmp_handle, &efi_fmp_guid, &efi_test_fmp, NULL); if (ret != EFI_SUCCESS) { efi_st_error("Failed to install FMP\n"); return EFI_ST_FAILURE; } esrt = lib_test_get_esrt(); if (!esrt) { efi_st_error("ESRT table not present\n"); return EFI_ST_FAILURE; } entry_delta = esrt->fw_resource_count - base_entry_count; if (entry_delta != TEST_ESRT_NUM_ENTRIES) { efi_st_error("ESRT mismatch in new entry count (%d), expected (%d).\n", entry_delta, TEST_ESRT_NUM_ENTRIES); return EFI_ST_FAILURE; } for (u32 idx = 0; idx < TEST_ESRT_NUM_ENTRIES; idx++) if (!lib_test_check_uuid_entry(esrt, &static_img_info[idx])) { efi_st_error("ESRT entry mismatch\n"); return EFI_ST_FAILURE; } return EFI_ST_SUCCESS; } EFI_UNIT_TEST(esrt) = { .name = "esrt", .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, .setup = setup, .execute = execute, .teardown = teardown, };