123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * efi_selftest_loadimage
- *
- * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
- *
- * This test checks the LoadImage and StartImage boot service.
- *
- * The efi_selftest_miniapp_exit.efi application is loaded via a file device
- * path and started.
- */
- #include <efi_selftest.h>
- /* Include containing the efi_selftest_miniapp_exit.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 FILE_NAME L"app.efi"
- #define VOLUME_NAME L"EfiDisk"
- static struct efi_boot_services *boottime;
- static efi_handle_t handle_image;
- static efi_handle_t handle_volume;
- static const efi_guid_t guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID;
- static const efi_guid_t guid_simple_file_system_protocol =
- EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
- static const efi_guid_t guid_file_info = EFI_FILE_INFO_GUID;
- static const efi_guid_t guid_file_system_info = EFI_FILE_SYSTEM_INFO_GUID;
- /* 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[];
- };
- /* File info including file name */
- struct file_info {
- struct efi_file_info info;
- u16 file_name[sizeof(FILE_NAME)];
- };
- /* File system info including volume name */
- struct file_system_info {
- struct efi_file_system_info info;
- u16 file_name[sizeof(VOLUME_NAME)];
- };
- /* Compressed file image */
- static struct compressed_file_image img = EFI_ST_DISK_IMG;
- /* Pointer to decompressed file image */
- static u8 *image;
- /* File info */
- static struct file_info priv_file_info = {
- {
- .size = sizeof(struct file_info),
- .attribute = EFI_FILE_READ_ONLY,
- },
- FILE_NAME,
- };
- /* Pointer to file info */
- struct efi_file_info *file_info = &priv_file_info.info;
- /* Volume device path */
- static struct {
- struct efi_device_path_vendor vendor;
- struct efi_device_path end;
- } __packed dp_volume = {
- .vendor = {
- .dp = {
- .type = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
- .sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
- .length = sizeof(struct efi_device_path_vendor),
- },
- .guid = EFI_GUID(0x4f9a0ebf, 0xa179, 0x88a6, 0x25, 0x68,
- 0x10, 0x72, 0xb1, 0x93, 0x51, 0x71),
- },
- .end = {
- .type = DEVICE_PATH_TYPE_END,
- .sub_type = DEVICE_PATH_SUB_TYPE_END,
- .length = sizeof(struct efi_device_path),
- }
- };
- /* File device path */
- static struct {
- struct efi_device_path_vendor vendor;
- struct efi_device_path path;
- u16 file[sizeof(FILE_NAME)];
- struct efi_device_path end;
- } __packed dp_file = {
- .vendor = {
- .dp = {
- .type = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
- .sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
- .length = sizeof(struct efi_device_path_vendor),
- },
- .guid = EFI_GUID(0x4f9a0ebf, 0xa179, 0x88a6, 0x25, 0x68,
- 0x10, 0x72, 0xb1, 0x93, 0x51, 0x71),
- },
- .path = {
- .type = DEVICE_PATH_TYPE_MEDIA_DEVICE,
- .sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH,
- .length = sizeof(struct efi_device_path) + sizeof(dp_file.file),
- },
- .file = FILE_NAME,
- .end = {
- .type = DEVICE_PATH_TYPE_END,
- .sub_type = DEVICE_PATH_SUB_TYPE_END,
- .length = sizeof(struct efi_device_path),
- }
- };
- /* File system info */
- static struct file_system_info priv_file_system_info = {
- {
- .size = sizeof(struct file_system_info),
- .read_only = true,
- .volume_size = 0x100000,
- .free_space = 0x0,
- .block_size = 0x200,
- },
- VOLUME_NAME
- };
- /* Pointer to file system info */
- static struct efi_file_system_info *file_system_info =
- &priv_file_system_info.info;
- /* Forward definitions of file and file system functions */
- static efi_status_t EFIAPI open_volume
- (struct efi_simple_file_system_protocol *this,
- struct efi_file_handle **root);
- static efi_status_t EFIAPI open
- (struct efi_file_handle *this,
- struct efi_file_handle **new_handle,
- u16 *file_name, u64 open_mode, u64 attributes);
- static efi_status_t EFIAPI close(struct efi_file_handle *this);
- static efi_status_t EFIAPI delete(struct efi_file_handle *this);
- static efi_status_t EFIAPI read
- (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer);
- static efi_status_t EFIAPI write
- (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer);
- static efi_status_t EFIAPI getpos(struct efi_file_handle *this, u64 *pos);
- static efi_status_t EFIAPI setpos(struct efi_file_handle *this, u64 pos);
- static efi_status_t EFIAPI getinfo
- (struct efi_file_handle *this, const efi_guid_t *info_type,
- efi_uintn_t *buffer_size, void *buffer);
- static efi_status_t EFIAPI setinfo
- (struct efi_file_handle *this, const efi_guid_t *info_type,
- efi_uintn_t buffer_size, void *buffer);
- static efi_status_t EFIAPI flush(struct efi_file_handle *this);
- /* Internal information about status of file system */
- static struct {
- /* Difference of volume open count minus volume close count */
- int volume_open_count;
- /* Difference of file open count minus file close count */
- int file_open_count;
- /* File size */
- u64 file_size;
- /* Current position in file */
- u64 file_pos;
- } priv;
- /* EFI_FILE_PROTOCOL for file */
- static struct efi_file_handle file = {
- .rev = 0x00010000,
- .open = open,
- .close = close,
- .delete = delete,
- .read = read,
- .write = write,
- .getpos = getpos,
- .setpos = setpos,
- .getinfo = getinfo,
- .setinfo = setinfo,
- .flush = flush,
- };
- /* EFI_FILE_PROTOCOL for root directory */
- static struct efi_file_handle volume = {
- .rev = 0x00010000,
- .open = open,
- .close = close,
- .delete = delete,
- .read = read,
- .write = write,
- .getpos = getpos,
- .setpos = setpos,
- .getinfo = getinfo,
- .setinfo = setinfo,
- .flush = flush,
- };
- /* EFI_SIMPLE_FILE_SYSTEM_PROTOCOL of the block device */
- struct efi_simple_file_system_protocol file_system = {
- .rev = 0x00010000,
- .open_volume = open_volume,
- };
- static efi_status_t EFIAPI open_volume
- (struct efi_simple_file_system_protocol *this,
- struct efi_file_handle **root)
- {
- if (this != &file_system || !root)
- return EFI_INVALID_PARAMETER;
- *root = &volume;
- priv.volume_open_count++;
- return EFI_SUCCESS;
- }
- static efi_status_t EFIAPI open
- (struct efi_file_handle *this,
- struct efi_file_handle **new_handle,
- u16 *file_name, u64 open_mode, u64 attributes)
- {
- if (this != &volume)
- return EFI_INVALID_PARAMETER;
- *new_handle = &file;
- priv.file_pos = 0;
- priv.file_open_count++;
- return EFI_SUCCESS;
- }
- static efi_status_t EFIAPI close(struct efi_file_handle *this)
- {
- if (this == &file)
- priv.file_open_count--;
- else if (this == &volume)
- priv.volume_open_count--;
- else
- return EFI_INVALID_PARAMETER;
- return EFI_SUCCESS;
- }
- static efi_status_t EFIAPI delete(struct efi_file_handle *this)
- {
- if (this != &file)
- return EFI_INVALID_PARAMETER;
- return EFI_UNSUPPORTED;
- }
- static efi_status_t EFIAPI read
- (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer)
- {
- if (this != &file)
- return EFI_INVALID_PARAMETER;
- if (priv.file_pos >= img.length)
- *buffer_size = 0;
- else if (priv.file_pos + *buffer_size > img.length)
- *buffer_size = img.length - priv.file_pos;
- boottime->copy_mem(buffer, &image[priv.file_pos], *buffer_size);
- priv.file_pos += *buffer_size;
- return EFI_SUCCESS;
- }
- static efi_status_t EFIAPI write
- (struct efi_file_handle *this, efi_uintn_t *buffer_size, void *buffer)
- {
- if (this != &file)
- return EFI_INVALID_PARAMETER;
- return EFI_UNSUPPORTED;
- }
- static efi_status_t EFIAPI getpos(struct efi_file_handle *this, u64 *pos)
- {
- if (this != &file)
- return EFI_INVALID_PARAMETER;
- *pos = priv.file_pos;
- return EFI_SUCCESS;
- }
- static efi_status_t EFIAPI setpos(struct efi_file_handle *this, u64 pos)
- {
- if (this != &file)
- return EFI_INVALID_PARAMETER;
- priv.file_pos = pos;
- return EFI_SUCCESS;
- }
- static efi_status_t EFIAPI getinfo
- (struct efi_file_handle *this, const efi_guid_t *info_type,
- efi_uintn_t *buffer_size, void *buffer)
- {
- if (this == &file) {
- if (memcmp(info_type, &guid_file_info, sizeof(efi_guid_t)))
- return EFI_INVALID_PARAMETER;
- if (*buffer_size >= sizeof(struct file_info)) {
- boottime->copy_mem(buffer, file_info,
- sizeof(struct file_info));
- } else {
- *buffer_size = sizeof(struct file_info);
- return EFI_BUFFER_TOO_SMALL;
- }
- } else if (this == &volume) {
- if (memcmp(info_type, &guid_file_system_info,
- sizeof(efi_guid_t)))
- return EFI_INVALID_PARAMETER;
- if (*buffer_size >= sizeof(struct file_system_info)) {
- boottime->copy_mem(buffer, file_system_info,
- sizeof(struct file_system_info));
- } else {
- *buffer_size = sizeof(struct file_system_info);
- return EFI_BUFFER_TOO_SMALL;
- }
- } else {
- return EFI_INVALID_PARAMETER;
- }
- return EFI_SUCCESS;
- }
- static efi_status_t EFIAPI setinfo
- (struct efi_file_handle *this, const efi_guid_t *info_type,
- efi_uintn_t buffer_size, void *buffer)
- {
- if (this != &file)
- return EFI_INVALID_PARAMETER;
- return EFI_UNSUPPORTED;
- }
- static efi_status_t EFIAPI flush(struct efi_file_handle *this)
- {
- if (this != &file)
- return EFI_INVALID_PARAMETER;
- return EFI_UNSUPPORTED;
- }
- /*
- * 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;
- priv.file_size = img.length;
- file_info->file_size = img.length;
- return ret;
- }
- /*
- * Setup unit test.
- *
- * Decompress application image and provide a handle for the in memory block
- * device.
- *
- * @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)
- {
- efi_status_t ret;
- handle_image = handle;
- boottime = systable->boottime;
- /* Load the application image into memory */
- decompress(&image);
- ret = boottime->install_protocol_interface
- (&handle_volume, &guid_device_path, EFI_NATIVE_INTERFACE,
- &dp_volume);
- if (ret != EFI_SUCCESS) {
- efi_st_error("Failed to install device path\n");
- return EFI_ST_FAILURE;
- }
- ret = boottime->install_protocol_interface
- (&handle_volume, &guid_simple_file_system_protocol,
- EFI_NATIVE_INTERFACE, &file_system);
- if (ret != EFI_SUCCESS) {
- efi_st_error("Failed to install simple file system protocol\n");
- return EFI_ST_FAILURE;
- }
- return EFI_ST_SUCCESS;
- }
- /*
- * Tear down unit test.
- *
- * Uninstall protocols and free memory.
- *
- * @return: EFI_ST_SUCCESS for success
- */
- static int teardown(void)
- {
- efi_status_t ret = EFI_ST_SUCCESS;
- if (handle_volume) {
- ret = boottime->uninstall_protocol_interface
- (handle_volume, &guid_simple_file_system_protocol,
- &file_system);
- if (ret != EFI_SUCCESS) {
- efi_st_error
- ("Failed to uninstall simple file system protocol\n");
- return EFI_ST_FAILURE;
- }
- ret = boottime->uninstall_protocol_interface
- (handle_volume, &guid_device_path, &dp_volume);
- if (ret != EFI_SUCCESS) {
- efi_st_error
- ("Failed to uninstall device path protocol\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.
- *
- * Load and start the application image.
- *
- * @return: EFI_ST_SUCCESS for success
- */
- static int execute(void)
- {
- efi_status_t ret;
- efi_handle_t handle;
- ret = boottime->load_image(false, handle_image, &dp_file.vendor.dp,
- NULL, 0, &handle);
- if (ret != EFI_SUCCESS) {
- efi_st_error("Failed to load image\n");
- return EFI_ST_FAILURE;
- }
- ret = boottime->start_image(handle, NULL, NULL);
- if (ret != EFI_UNSUPPORTED) {
- efi_st_error("Wrong return value from application\n");
- return EFI_ST_FAILURE;
- }
- if (priv.file_open_count) {
- efi_st_error("File open count = %d, expected 0\n",
- priv.file_open_count);
- return EFI_ST_FAILURE;
- }
- if (priv.volume_open_count) {
- efi_st_error("Volume open count = %d, expected 0\n",
- priv.volume_open_count);
- return EFI_ST_FAILURE;
- }
- return EFI_ST_SUCCESS;
- }
- EFI_UNIT_TEST(loadimage) = {
- .name = "load image from file",
- .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
- .setup = setup,
- .execute = execute,
- .teardown = teardown,
- };
|