// SPDX-License-Identifier: GPL-2.0+ /* * efi_selftest_load_file * * Copyright (c) 2020 Heinrich Schuchardt * * This test checks the handling of the LOAD_FILE and the LOAD_FILE2 protocol * by the LoadImage() service. */ #include /* Include containing the miniapp.efi application */ #include "efi_miniapp_file_image_exit.h" /* Block size of compressed disk image */ #define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8 /* Binary logarithm of the block size */ #define LB_BLOCK_SIZE 9 #define GUID_VENDOR \ EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d, \ 0x08, 0x72, 0x81, 0x9c, 0x65, 0xfc, 0xbb, 0xd1) #define GUID_VENDOR2 \ EFI_GUID(0xdbca4c98, 0x6cb0, 0x694d, \ 0x08, 0x72, 0x81, 0x9c, 0x65, 0xfc, 0xbb, 0xd2) #define FILE_NAME_SIZE 16 static const efi_guid_t efi_st_guid_load_file_protocol = EFI_LOAD_FILE_PROTOCOL_GUID; static const efi_guid_t efi_st_guid_load_file2_protocol = EFI_LOAD_FILE2_PROTOCOL_GUID; static const efi_guid_t efi_st_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID; static efi_handle_t image_handle; static struct efi_boot_services *boottime; static efi_handle_t handle_lf; static efi_handle_t handle_lf2; /* One 8 byte block of the compressed disk image */ struct line { size_t addr; char *line; }; /* Compressed file image */ struct compressed_file_image { size_t length; struct line lines[]; }; static struct compressed_file_image img = EFI_ST_DISK_IMG; static int load_file_call_count; static int load_file2_call_count; /* Decompressed file image */ static u8 *image; static struct { struct efi_device_path_vendor v; struct efi_device_path d; } dp_lf_prot = { { { DEVICE_PATH_TYPE_HARDWARE_DEVICE, DEVICE_PATH_SUB_TYPE_VENDOR, sizeof(struct efi_device_path_vendor), }, GUID_VENDOR, }, { DEVICE_PATH_TYPE_END, DEVICE_PATH_SUB_TYPE_END, sizeof(struct efi_device_path), }, }; static struct { struct efi_device_path_vendor v; struct efi_device_path_file_path f; u16 file_name[FILE_NAME_SIZE]; struct efi_device_path e; } dp_lf_file = { { { DEVICE_PATH_TYPE_HARDWARE_DEVICE, DEVICE_PATH_SUB_TYPE_VENDOR, sizeof(struct efi_device_path_vendor), }, GUID_VENDOR, }, { { DEVICE_PATH_TYPE_MEDIA_DEVICE, DEVICE_PATH_SUB_TYPE_FILE_PATH, sizeof(struct efi_device_path_file_path) + FILE_NAME_SIZE * sizeof(u16), } }, u"\\lf.efi", { DEVICE_PATH_TYPE_END, DEVICE_PATH_SUB_TYPE_END, sizeof(struct efi_device_path), }, }; struct efi_device_path *dp_lf_file_remainder = &dp_lf_file.f.dp; static struct { struct efi_device_path_vendor v; struct efi_device_path d; } dp_lf2_prot = { { { DEVICE_PATH_TYPE_HARDWARE_DEVICE, DEVICE_PATH_SUB_TYPE_VENDOR, sizeof(struct efi_device_path_vendor), }, GUID_VENDOR2, }, { DEVICE_PATH_TYPE_END, DEVICE_PATH_SUB_TYPE_END, sizeof(struct efi_device_path), }, }; static struct { struct efi_device_path_vendor v; struct efi_device_path_file_path f; u16 file_name[FILE_NAME_SIZE]; struct efi_device_path e; } dp_lf2_file = { { { DEVICE_PATH_TYPE_HARDWARE_DEVICE, DEVICE_PATH_SUB_TYPE_VENDOR, sizeof(struct efi_device_path_vendor), }, GUID_VENDOR2, }, { { DEVICE_PATH_TYPE_MEDIA_DEVICE, DEVICE_PATH_SUB_TYPE_FILE_PATH, sizeof(struct efi_device_path_file_path) + FILE_NAME_SIZE * sizeof(u16), } }, u"\\lf2.efi", { DEVICE_PATH_TYPE_END, DEVICE_PATH_SUB_TYPE_END, sizeof(struct efi_device_path), }, }; struct efi_device_path *dp_lf2_file_remainder = &dp_lf2_file.f.dp; /* * Decompress the disk image. * * @image decompressed disk image * Return: status code */ static efi_status_t decompress(u8 **image) { u8 *buf; size_t i; size_t addr; size_t len; efi_status_t ret; ret = boottime->allocate_pool(EFI_LOADER_DATA, img.length, (void **)&buf); if (ret != EFI_SUCCESS) { efi_st_error("Out of memory\n"); return ret; } boottime->set_mem(buf, img.length, 0); for (i = 0; ; ++i) { if (!img.lines[i].line) break; addr = img.lines[i].addr; len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE; if (addr + len > img.length) len = img.length - addr; boottime->copy_mem(buf + addr, img.lines[i].line, len); } *image = buf; return ret; } /* * load_file() - LoadFile() service of a EFI_LOAD_FILE_PROTOCOL * * @this: instance of EFI_LOAD_FILE_PROTOCOL * @file_path: remaining device path * @boot_policy: true if called by boot manager * @buffer_size: (required) buffer size * @buffer: buffer to which the file is to be loaded */ static efi_status_t EFIAPI load_file(struct efi_load_file_protocol *this, struct efi_device_path *file_path, bool boot_policy, efi_uintn_t *buffer_size, void *buffer) { ++load_file_call_count; if (memcmp(file_path, dp_lf_file_remainder, sizeof(struct efi_device_path_file_path) + FILE_NAME_SIZE * sizeof(u16) + sizeof(struct efi_device_path))) { efi_st_error("Wrong remaining device path\n"); return EFI_NOT_FOUND; } if (this->load_file != load_file) { efi_st_error("wrong this\n"); return EFI_INVALID_PARAMETER; } if (*buffer_size < img.length) { *buffer_size = img.length; return EFI_BUFFER_TOO_SMALL; } memcpy(buffer, image, img.length); *buffer_size = img.length; return EFI_SUCCESS; } /* * load_file2() - LoadFile() service of a EFI_LOAD_FILE2_PROTOCOL * * @this: instance of EFI_LOAD_FILE2_PROTOCOL * @file_path: remaining device path * @boot_policy: true if called by boot manager * @buffer_size: (required) buffer size * @buffer: buffer to which the file is to be loaded */ static efi_status_t EFIAPI load_file2(struct efi_load_file_protocol *this, struct efi_device_path *file_path, bool boot_policy, efi_uintn_t *buffer_size, void *buffer) { ++load_file2_call_count; if (memcmp(file_path, dp_lf2_file_remainder, sizeof(struct efi_device_path_file_path) + FILE_NAME_SIZE * sizeof(u16) + sizeof(struct efi_device_path))) { efi_st_error("Wrong remaining device path\n"); return EFI_NOT_FOUND; } if (this->load_file != load_file2) { efi_st_error("wrong this\n"); return EFI_INVALID_PARAMETER; } if (boot_policy) { efi_st_error("LOAD_FILE2 called with boot_policy = true"); return EFI_INVALID_PARAMETER; } if (*buffer_size < img.length) { *buffer_size = img.length; return EFI_BUFFER_TOO_SMALL; } memcpy(buffer, image, img.length); *buffer_size = img.length; return EFI_SUCCESS; } static struct efi_load_file_protocol lf_prot = {load_file}; static struct efi_load_file_protocol lf2_prot = {load_file2}; /* * Setup unit test. * * Install an EFI_LOAD_FILE_PROTOCOL and an EFI_LOAD_FILE2_PROTOCOL. * * @handle: handle of the loaded image * @systable: system table * Return: EFI_ST_SUCCESS for success */ static int efi_st_load_file_setup(const efi_handle_t handle, const struct efi_system_table *systable) { efi_status_t ret; image_handle = handle; boottime = systable->boottime; /* Load the application image into memory */ decompress(&image); ret = boottime->install_multiple_protocol_interfaces( &handle_lf, &efi_st_guid_device_path, &dp_lf_prot, &efi_st_guid_load_file_protocol, &lf_prot, NULL); if (ret != EFI_SUCCESS) { efi_st_error("InstallMultipleProtocolInterfaces failed\n"); return EFI_ST_FAILURE; } ret = boottime->install_multiple_protocol_interfaces( &handle_lf2, &efi_st_guid_device_path, &dp_lf2_prot, &efi_st_guid_load_file2_protocol, &lf2_prot, NULL); if (ret != EFI_SUCCESS) { efi_st_error("InstallMultipleProtocolInterfaces failed\n"); return EFI_ST_FAILURE; } return EFI_ST_SUCCESS; } /* * Tear down unit test. * * Return: EFI_ST_SUCCESS for success */ static int efi_st_load_file_teardown(void) { efi_status_t ret = EFI_ST_SUCCESS; if (handle_lf) { ret = boottime->uninstall_multiple_protocol_interfaces( handle_lf, &efi_st_guid_device_path, &dp_lf_prot, &efi_st_guid_load_file_protocol, &lf_prot, NULL); if (ret != EFI_SUCCESS) { efi_st_error( "UninstallMultipleProtocolInterfaces failed\n"); return EFI_ST_FAILURE; } } if (handle_lf2) { ret = boottime->uninstall_multiple_protocol_interfaces( handle_lf2, &efi_st_guid_device_path, &dp_lf2_prot, &efi_st_guid_load_file2_protocol, &lf2_prot, NULL); if (ret != EFI_SUCCESS) { efi_st_error( "UninstallMultipleProtocolInterfaces failed\n"); return EFI_ST_FAILURE; } } if (image) { ret = boottime->free_pool(image); if (ret != EFI_SUCCESS) { efi_st_error("Failed to free image\n"); return EFI_ST_FAILURE; } } return ret; } /* * Execute unit test. * * Try loading an image via the EFI_LOAD_FILE_PROTOCOL and the * EFI_LOAD_FILE2_PROTOCOL. Finally execute the image. * * Return: EFI_ST_SUCCESS for success */ static int efi_st_load_file_execute(void) { efi_status_t ret; efi_handle_t handle; efi_uintn_t exit_data_size = 0; u16 *exit_data = NULL; u16 expected_text[] = EFI_ST_SUCCESS_STR; load_file_call_count = 0; load_file2_call_count = 0; handle = NULL; ret = boottime->load_image(true, image_handle, &dp_lf_file.v.dp, NULL, 0, &handle); if (ret != EFI_SUCCESS) { efi_st_error("Failed to load image\n"); return EFI_ST_FAILURE; } if (load_file2_call_count || !load_file_call_count) { efi_st_error("Wrong image loaded\n"); return EFI_ST_FAILURE; } ret = boottime->unload_image(handle); if (ret != EFI_SUCCESS) { efi_st_error("Failed to unload image\n"); return EFI_ST_FAILURE; } load_file_call_count = 0; load_file2_call_count = 0; handle = NULL; ret = boottime->load_image(false, image_handle, &dp_lf_file.v.dp, NULL, 0, &handle); if (ret != EFI_SUCCESS) { efi_st_error("Failed to load image\n"); return EFI_ST_FAILURE; } if (load_file2_call_count || !load_file_call_count) { efi_st_error("Wrong image loaded\n"); return EFI_ST_FAILURE; } ret = boottime->unload_image(handle); if (ret != EFI_SUCCESS) { efi_st_error("Failed to unload image\n"); return EFI_ST_FAILURE; } ret = boottime->load_image(true, image_handle, &dp_lf2_file.v.dp, NULL, 0, &handle); if (ret != EFI_NOT_FOUND) { efi_st_error( "Boot manager should not use LOAD_FILE2_PROTOCOL\n"); return EFI_ST_FAILURE; } load_file_call_count = 0; load_file2_call_count = 0; handle = NULL; ret = boottime->load_image(false, image_handle, &dp_lf2_file.v.dp, NULL, 0, &handle); if (ret != EFI_SUCCESS) { efi_st_error("Failed to load image\n"); return EFI_ST_FAILURE; } if (!load_file2_call_count || load_file_call_count) { efi_st_error("Wrong image loaded\n"); return EFI_ST_FAILURE; } ret = boottime->start_image(handle, &exit_data_size, &exit_data); if (ret != EFI_UNSUPPORTED) { efi_st_error("Wrong return value from application\n"); return EFI_ST_FAILURE; } if (!exit_data || exit_data_size != sizeof(expected_text) || memcmp(exit_data, expected_text, sizeof(expected_text))) { efi_st_error("Incorrect exit data\n"); return EFI_ST_FAILURE; } ret = boottime->free_pool(exit_data); if (ret != EFI_SUCCESS) { efi_st_error("Failed to free exit data\n"); return EFI_ST_FAILURE; } return EFI_ST_SUCCESS; } EFI_UNIT_TEST(load_file_protocol) = { .name = "load file protocol", .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, .setup = efi_st_load_file_setup, .execute = efi_st_load_file_execute, .teardown = efi_st_load_file_teardown, };