| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Test ESRT tables support |
| * |
| * Copyright (C) 2021 Arm Ltd. |
| */ |
| #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, |
| }; |