From 2fdfb802e30a1fbd65a830a283d7bd87631f08c0 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 19 Jun 2025 10:34:01 +0200 Subject: efi_loader: disk: add EFI_PARTITION_INFO_PROTOCOL support The UEFI 2.10 specification mentions that this protocol shall be installed along with EFI_BLOCK_IO_PROTOCOL. It provides cached partition information for MBR and GPT partition types. This patch just implements support for GPT partition types. The legacy MBR partition types is only needed for backward compatibility and can be added as a follow-up if needed, to make it fully compliant with the EFI spec. Signed-off-by: Javier Martinez Canillas Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_disk.c | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 47b583cc5e1..130c4db9606 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -26,6 +26,7 @@ struct efi_system_partition efi_system_partition = { const efi_guid_t efi_block_io_guid = EFI_BLOCK_IO_PROTOCOL_GUID; const efi_guid_t efi_system_partition_guid = PARTITION_SYSTEM_GUID; +const efi_guid_t efi_partition_info_guid = EFI_PARTITION_INFO_PROTOCOL_GUID; /** * struct efi_disk_obj - EFI disk object @@ -35,6 +36,7 @@ const efi_guid_t efi_system_partition_guid = PARTITION_SYSTEM_GUID; * @media: block I/O media information * @dp: device path to the block device * @volume: simple file system protocol of the partition + * @info: EFI partition info protocol interface */ struct efi_disk_obj { struct efi_object header; @@ -42,6 +44,7 @@ struct efi_disk_obj { struct efi_block_io_media media; struct efi_device_path *dp; struct efi_simple_file_system_protocol *volume; + struct efi_partition_info info; }; /** @@ -426,6 +429,7 @@ static efi_status_t efi_disk_add_dev( /* Fill in object data */ if (part_info) { struct efi_device_path *node = efi_dp_part_node(desc, part); + struct efi_partition_info *info = &diskobj->info; struct efi_handler *handler; void *protocol_interface; @@ -454,18 +458,48 @@ static efi_status_t efi_disk_add_dev( goto error; } + info->revision = EFI_PARTITION_INFO_PROTOCOL_REVISION; + + switch (desc->part_type) { +#if CONFIG_IS_ENABLED(EFI_PARTITION) + case PART_TYPE_EFI: + info->type = PARTITION_TYPE_GPT; + ret = part_get_gpt_pte(desc, part, &info->info.gpt); + if (ret) { + log_debug("get PTE for part %d failed %ld\n", + part, ret); + goto error; + } + break; +#endif +#if CONFIG_IS_ENABLED(DOS_PARTITION) + case PART_TYPE_DOS: + info->type = PARTITION_TYPE_MBR; + + /* TODO: implement support for MBR partition types */ + log_debug("EFI_PARTITION_INFO_PROTOCOL doesn't support MBR\n"); + break; +#endif + default: + info->type = PARTITION_TYPE_OTHER; + break; + } + diskobj->dp = efi_dp_append_node(dp_parent, node); efi_free_pool(node); diskobj->media.last_block = part_info->size - 1; - if (part_info->bootable & PART_EFI_SYSTEM_PARTITION) + if (part_info->bootable & PART_EFI_SYSTEM_PARTITION) { esp_guid = &efi_system_partition_guid; + info->system = 1; + } + } else { diskobj->dp = efi_dp_from_part(desc, part); diskobj->media.last_block = desc->lba - 1; } /* - * Install the device path and the block IO protocol. + * Install the device path, the block IO and partition info protocols. * * InstallMultipleProtocolInterfaces() checks if the device path is * already installed on an other handle and returns EFI_ALREADY_STARTED @@ -476,6 +510,7 @@ static efi_status_t efi_disk_add_dev( &handle, &efi_guid_device_path, diskobj->dp, &efi_block_io_guid, &diskobj->ops, + &efi_partition_info_guid, &diskobj->info, /* * esp_guid must be last entry as it * can be NULL. Its interface is NULL. -- cgit v1.2.3 From 4e103418bed6374b9aa43d067cee704d7e745dc9 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 19 Jun 2025 10:34:02 +0200 Subject: efi_selftest: Add basic partition info check to block io test Test the EFI_PARTITION_INFO_PROTOCOL in the existing EFI_BLOCK_IO_PROTOCOL unit test. It is fairly basic, since it only checks that the values of the struct efi_partition_info .revision, .type and .system fields are correct. It doesn't check the MBR partition record information, because that's not supported by the EFI_PARTITION_INFO_PROTOCOL implementation yet. The test can be extended once the support is implemented, or if the in-memory disk image used for the test is modified to have a GPT partition type instead. Suggested-by: Tom Rini Signed-off-by: Javier Martinez Canillas Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- lib/efi_selftest/efi_selftest_block_device.c | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'lib') diff --git a/lib/efi_selftest/efi_selftest_block_device.c b/lib/efi_selftest/efi_selftest_block_device.c index a367e8b89d1..f145e58a267 100644 --- a/lib/efi_selftest/efi_selftest_block_device.c +++ b/lib/efi_selftest/efi_selftest_block_device.c @@ -18,6 +18,7 @@ #include #include "efi_selftest_disk_image.h" #include +#include /* Block size of compressed disk image */ #define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8 @@ -29,6 +30,7 @@ static struct efi_boot_services *boottime; static const efi_guid_t block_io_protocol_guid = EFI_BLOCK_IO_PROTOCOL_GUID; static const efi_guid_t guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID; +static const efi_guid_t partition_info_guid = EFI_PARTITION_INFO_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_system_info = EFI_FILE_SYSTEM_INFO_GUID; @@ -310,6 +312,7 @@ static int execute(void) struct efi_file_system_info info; u16 label[12]; } system_info; + struct efi_partition_info *part_info; efi_uintn_t buf_size; char buf[16] __aligned(ARCH_DMA_MINALIGN); u32 part1_size; @@ -375,6 +378,33 @@ static int execute(void) part1_size - 1); return EFI_ST_FAILURE; } + + /* Open the partition information protocol */ + ret = boottime->open_protocol(handle_partition, + &partition_info_guid, + (void **)&part_info, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to open partition information protocol\n"); + return EFI_ST_FAILURE; + } + /* Check that cached partition information is the expected */ + if (part_info->revision != EFI_PARTITION_INFO_PROTOCOL_REVISION) { + efi_st_error("Partition info revision %x, expected %x\n", + part_info->revision, EFI_PARTITION_INFO_PROTOCOL_REVISION); + return EFI_ST_FAILURE; + } + if (part_info->type != PARTITION_TYPE_MBR) { + efi_st_error("Partition info type %x, expected %x\n", + part_info->type, PARTITION_TYPE_MBR); + return EFI_ST_FAILURE; + } + if (part_info->system != 0) { + efi_st_error("Partition info system %x, expected 0\n", + part_info->system); + return EFI_ST_FAILURE; + } + /* Open the simple file system protocol */ ret = boottime->open_protocol(handle_partition, &guid_simple_file_system_protocol, -- cgit v1.2.3 From 9d95a35715fcb8e81ee423e31273489a47ed1563 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Wed, 2 Jul 2025 11:01:36 +0100 Subject: efi_loader: Prevent dereferencing NULL pointer Taking the first goto error: in file_open could either result in an attempt to dereference fh when NULL or else free fh->path which has not been assigned to and so will be unknown. Avoid both of these problems by passing path to free instead of fh->path. This issue found by Smatch. Reviewed-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt Signed-off-by: Andrew Goodbody Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index 7d81da8f2d8..19b43c4a625 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -248,7 +248,7 @@ static struct efi_file_handle *file_open(struct file_system *fs, return &fh->base; error: - free(fh->path); + free(path); free(fh); return NULL; } -- cgit v1.2.3 From 5753dc3f6572d42057a262f0e57e904e5c9cd9bc Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Wed, 2 Jul 2025 11:01:37 +0100 Subject: efi_loader: Prevent dereference of uninitialised variable If phandler is returned as NULL from efi_search_protocol then protocol_interface is never assigned to. Instead return EFI_UNSUPPORTED as per the spec. This issue found by Smatch. Also eliminate the use of the variable protocol_interface as it is not needed. Signed-off-by: Andrew Goodbody Reviewed-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_http.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_http.c b/lib/efi_loader/efi_http.c index 189317fe2d2..9a0f2675132 100644 --- a/lib/efi_loader/efi_http.c +++ b/lib/efi_loader/efi_http.c @@ -453,7 +453,6 @@ static efi_status_t EFIAPI efi_http_service_binding_destroy_child( efi_status_t ret = EFI_SUCCESS; struct efi_http_instance *http_instance; struct efi_handler *phandler; - void *protocol_interface; if (num_instances == 0) return EFI_EXIT(EFI_NOT_FOUND); @@ -463,18 +462,18 @@ static efi_status_t EFIAPI efi_http_service_binding_destroy_child( efi_search_protocol(child_handle, &efi_http_guid, &phandler); - if (phandler) - protocol_interface = phandler->protocol_interface; + if (!phandler) + return EFI_EXIT(EFI_UNSUPPORTED); ret = efi_delete_handle(child_handle); if (ret != EFI_SUCCESS) return EFI_EXIT(ret); - http_instance = (struct efi_http_instance *)protocol_interface; + http_instance = phandler->protocol_interface; efi_free_pool(http_instance->http_load_addr); http_instance->http_load_addr = NULL; - free(protocol_interface); + free(phandler->protocol_interface); num_instances--; -- cgit v1.2.3 From 9a64eecad675f99f1ddf14184433a46728f7e047 Mon Sep 17 00:00:00 2001 From: Andrew Goodbody Date: Wed, 2 Jul 2025 11:01:38 +0100 Subject: efi_loader: Prevent free of uninitialised pointer Taking a goto to out_of_resources before receive_lengths is assigned will result in an attempt to free an unitialised pointer. Instead initialise receive_lengths to NULL on declaration to prevent this from occurring. This issue was found by Smatch. Reviewed-by: Heinrich Schuchardt Reviewed-by: Ilias Apalodimas Signed-off-by: Andrew Goodbody Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 86f0af9538c..b8a6e08ba8e 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -1131,7 +1131,7 @@ efi_status_t efi_net_register(struct udevice *dev) struct efi_net_obj *netobj; void *transmit_buffer = NULL; uchar **receive_buffer = NULL; - size_t *receive_lengths; + size_t *receive_lengths = NULL; int i, j; if (!dev) { -- cgit v1.2.3 From aaf7be96c2c14aa190d39e50fa3606389eafda8e Mon Sep 17 00:00:00 2001 From: "Ying-Chun Liu (PaulLiu)" Date: Thu, 3 Jul 2025 07:28:07 +0100 Subject: efi: add EFI_SYSTEM_TABLE_POINTER for debug Add EFI_SYSTEM_TABLE_POINTER structure for remote debugger to locate the address of EFI_SYSTEM_TABLE. This feature is described in UEFI SPEC version 2.10. Section 18.4.2. The implementation ensures support for hardware-assisted debugging and provides a standardized mechanism for debuggers to discover the EFI system table. Cc: Peter Robinson Cc: Simon Glass Signed-off-by: Ying-Chun Liu (PaulLiu) Reviewed-by: Ilias Apalodimas Tested-by: Heinrich Schuchardt Reviewed-by: Heinrich Schuchardt # change memset(systab_pointer, 0 ...) -> systab_pointer->crc32 = 0; Signed-off-by: Ilias Apalodimas --- lib/efi_loader/Kconfig | 9 +++++++++ lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_debug_support.c | 40 ++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_setup.c | 7 +++++++ 4 files changed, 57 insertions(+) create mode 100644 lib/efi_loader/efi_debug_support.c (limited to 'lib') diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 3dadbc54b58..077466f01f0 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -71,6 +71,15 @@ config EFI_SECURE_BOOT config EFI_SIGNATURE_SUPPORT bool +config EFI_DEBUG_SUPPORT + bool "EFI Debug Support" + default y if !HAS_BOARD_SIZE_LIMIT + help + Select this option if you want to setup the EFI Debug Support + Table and the EFI_SYSTEM_TABLE_POINTER which is used by the debug + agent or an external debugger to determine loaded image information + in a quiescent manner. + menu "UEFI services" config EFI_GET_TIME diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index cf050e5385d..51ccf1cd87e 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_EFI_RISCV_BOOT_PROTOCOL) += efi_riscv.o obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o obj-$(CONFIG_EFI_SIGNATURE_SUPPORT) += efi_signature.o obj-$(CONFIG_EFI_ECPT) += efi_conformance.o +obj-$(CONFIG_EFI_DEBUG_SUPPORT) += efi_debug_support.o EFI_VAR_SEED_FILE := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE)) $(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE) diff --git a/lib/efi_loader/efi_debug_support.c b/lib/efi_loader/efi_debug_support.c new file mode 100644 index 00000000000..649d21ef829 --- /dev/null +++ b/lib/efi_loader/efi_debug_support.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * EFI debug support + * + * Copyright (c) 2025 Ying-Chun Liu, Linaro Ltd. + */ + +#include +#include +#include + +struct efi_system_table_pointer __efi_runtime_data * systab_pointer = NULL; + +/** + * efi_initialize_system_table_pointer() - Initialize system table pointer + * + * Return: status code + */ +efi_status_t efi_initialize_system_table_pointer(void) +{ + /* Allocate efi_system_table_pointer structure with 4MB alignment. */ + systab_pointer = efi_alloc_aligned_pages(sizeof(struct efi_system_table_pointer), + EFI_RUNTIME_SERVICES_DATA, + SZ_4M); + + if (!systab_pointer) { + log_err("Installing EFI system table pointer failed\n"); + return EFI_OUT_OF_RESOURCES; + } + + systab_pointer->crc32 = 0; + + systab_pointer->signature = EFI_SYSTEM_TABLE_SIGNATURE; + systab_pointer->efi_system_table_base = (uintptr_t)&systab; + systab_pointer->crc32 = crc32(0, + (const unsigned char *)systab_pointer, + sizeof(struct efi_system_table_pointer)); + + return EFI_SUCCESS; +} diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 48f91da5df7..78c5059256a 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -278,6 +278,13 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out; + /* Initialize system table pointer */ + if (IS_ENABLED(CONFIG_EFI_DEBUG_SUPPORT)) { + ret = efi_initialize_system_table_pointer(); + if (ret != EFI_SUCCESS) + goto out; + } + if (IS_ENABLED(CONFIG_EFI_ECPT)) { ret = efi_ecpt_register(); if (ret != EFI_SUCCESS) -- cgit v1.2.3 From e7a85ec651ed5794eb9a837e1073f6b3146af501 Mon Sep 17 00:00:00 2001 From: "Ying-Chun Liu (PaulLiu)" Date: Thu, 3 Jul 2025 07:28:08 +0100 Subject: efi: add EFI_DEBUG_IMAGE_INFO_TABLE for debug EFI_DEBUG_IMAGE_INFO_TABLE is used to store EFI_LOADED_IMAGE for debug purpose. This commit adds the table to the EFI_CONFIGURATION_TABLE. This feature is described in UEFI Spec version 2.10. Section 18.4. The implementation ensures support for hardware-assisted debugging and provides a standardized mechanism for debuggers to discover and interact with system-level debug resources. Cc: Heinrich Schuchardt Cc: Peter Robinson Cc: Simon Glass Signed-off-by: Ying-Chun Liu (PaulLiu) Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_debug_support.c | 6 ++++++ lib/efi_loader/efi_setup.c | 11 +++++++++++ 2 files changed, 17 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_debug_support.c b/lib/efi_loader/efi_debug_support.c index 649d21ef829..597592fbd1c 100644 --- a/lib/efi_loader/efi_debug_support.c +++ b/lib/efi_loader/efi_debug_support.c @@ -11,6 +11,12 @@ struct efi_system_table_pointer __efi_runtime_data * systab_pointer = NULL; +struct efi_debug_image_info_table_header efi_m_debug_info_table_header = { + 0, + 0, + NULL +}; + /** * efi_initialize_system_table_pointer() - Initialize system table pointer * diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 78c5059256a..f06cf49e443 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -18,6 +18,9 @@ efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED; +const efi_guid_t efi_debug_image_info_table_guid = + EFI_DEBUG_IMAGE_INFO_TABLE_GUID; + /* * Allow unaligned memory access. * @@ -280,9 +283,17 @@ efi_status_t efi_init_obj_list(void) /* Initialize system table pointer */ if (IS_ENABLED(CONFIG_EFI_DEBUG_SUPPORT)) { + efi_guid_t debug_image_info_table_guid = + efi_debug_image_info_table_guid; + ret = efi_initialize_system_table_pointer(); if (ret != EFI_SUCCESS) goto out; + + ret = efi_install_configuration_table(&debug_image_info_table_guid, + &efi_m_debug_info_table_header); + if (ret != EFI_SUCCESS) + goto out; } if (IS_ENABLED(CONFIG_EFI_ECPT)) { -- cgit v1.2.3 From 3c08df58cc43c3ae8ec378ab0e32cfe548313c39 Mon Sep 17 00:00:00 2001 From: "Ying-Chun Liu (PaulLiu)" Date: Thu, 3 Jul 2025 07:28:09 +0100 Subject: lib: efi_loader: efi_memory.c: add efi_realloc() for realloc memory Add efi_realloc() for realloc memory that previously alloc by efi_alloc(). Note that if realloced memory is explicitly allocated as BootServicesData. Cc: Heinrich Schuchardt Cc: Ilias Apalodimas Cc: Peter Robinson Cc: Simon Glass Signed-off-by: Ying-Chun Liu (PaulLiu) Reviewed-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_memory.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 0828a47da61..6dfc698a247 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -668,6 +668,64 @@ void *efi_alloc(size_t size) return buf; } +/** + * efi_realloc() - reallocate boot services data pool memory + * + * Reallocate memory from pool for a new size and copy the data from old one. + * + * @ptr: pointer to old buffer + * @size: number of bytes to allocate + * Return: EFI status to indicate success or not + */ +efi_status_t efi_realloc(void **ptr, size_t size) +{ + efi_status_t ret; + void *new_ptr; + struct efi_pool_allocation *alloc; + u64 num_pages = efi_size_in_pages(size + + sizeof(struct efi_pool_allocation)); + size_t old_size; + + if (!*ptr) { + *ptr = efi_alloc(size); + if (*ptr) + return EFI_SUCCESS; + return EFI_OUT_OF_RESOURCES; + } + + ret = efi_check_allocated((uintptr_t)*ptr, true); + if (ret != EFI_SUCCESS) + return ret; + + alloc = container_of(*ptr, struct efi_pool_allocation, data); + + /* Check that this memory was allocated by efi_allocate_pool() */ + if (((uintptr_t)alloc & EFI_PAGE_MASK) || + alloc->checksum != checksum(alloc)) { + printf("%s: illegal realloc 0x%p\n", __func__, *ptr); + return EFI_INVALID_PARAMETER; + } + + /* Don't realloc. The actual size in pages is the same. */ + if (alloc->num_pages == num_pages) + return EFI_SUCCESS; + + old_size = alloc->num_pages * EFI_PAGE_SIZE - + sizeof(struct efi_pool_allocation); + + new_ptr = efi_alloc(size); + + /* copy old data to new alloced buffer */ + memcpy(new_ptr, *ptr, min(size, old_size)); + + /* free the old buffer */ + efi_free_pool(*ptr); + + *ptr = new_ptr; + + return EFI_SUCCESS; +} + /** * efi_free_pool() - free memory from pool * -- cgit v1.2.3 From 146546138af5966c97619797dc7f879c4857b00d Mon Sep 17 00:00:00 2001 From: "Ying-Chun Liu (PaulLiu)" Date: Thu, 3 Jul 2025 07:28:10 +0100 Subject: efi: add EFI_DEBUG_IMAGE_INFO for debug This commit adds the functionality of generate EFI_DEBUG_IMAGE_INFO while loading the image. This feature is described in UEFI Spec 2.10. Section 18.4.3. The implementation ensures support for hardware-assisted debugging and provides a standardized mechanism for debuggers to discover the load address of an EFI application. Cc: Ilias Apalodimas Cc: Peter Robinson Cc: Simon Glass Signed-off-by: Ying-Chun Liu (PaulLiu) Tested-by: Heinrich Schuchardt Acked-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_boottime.c | 7 ++ lib/efi_loader/efi_debug_support.c | 137 +++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) (limited to 'lib') diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 754bc6a6519..ddc935d2240 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2130,6 +2130,11 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy, *image_handle = NULL; free(info); } + + if (IS_ENABLED(CONFIG_EFI_DEBUG_SUPPORT) && *image_handle) + efi_core_new_debug_image_info_entry(EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL, + info, + *image_handle); error: return EFI_EXIT(ret); } @@ -3360,6 +3365,8 @@ efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle) ret = EFI_INVALID_PARAMETER; goto out; } + if (IS_ENABLED(CONFIG_EFI_DEBUG_SUPPORT)) + efi_core_remove_debug_image_info_entry(image_handle); switch (efiobj->type) { case EFI_OBJECT_TYPE_STARTED_IMAGE: /* Call the unload function */ diff --git a/lib/efi_loader/efi_debug_support.c b/lib/efi_loader/efi_debug_support.c index 597592fbd1c..186bdbce750 100644 --- a/lib/efi_loader/efi_debug_support.c +++ b/lib/efi_loader/efi_debug_support.c @@ -17,6 +17,13 @@ struct efi_debug_image_info_table_header efi_m_debug_info_table_header = { NULL }; +/* efi_m_max_table_entries is the maximum entries allocated for + * the efi_m_debug_info_table_header.efi_debug_image_info_table. + */ +static u32 efi_m_max_table_entries; + +#define EFI_DEBUG_TABLE_ENTRY_SIZE (sizeof(union efi_debug_image_info *)) + /** * efi_initialize_system_table_pointer() - Initialize system table pointer * @@ -44,3 +51,133 @@ efi_status_t efi_initialize_system_table_pointer(void) return EFI_SUCCESS; } + +/** + * efi_core_new_debug_image_info_entry() - Add a new efi_loaded_image structure to the + * efi_debug_image_info table. + * + * @image_info_type: type of debug image information + * @loaded_image: pointer to the loaded image protocol for the image + * being loaded + * @image_handle: image handle for the image being loaded + * + * Re-Allocates the table if it's not large enough to accommodate another + * entry. + * + * Return: status code + **/ +efi_status_t efi_core_new_debug_image_info_entry(u32 image_info_type, + struct efi_loaded_image *loaded_image, + efi_handle_t image_handle) +{ + union efi_debug_image_info **table; + u32 index; + u32 table_size; + efi_status_t ret; + + /* Set the flag indicating that we're in the process of updating + * the table. + */ + efi_m_debug_info_table_header.update_status |= + EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS; + + table = &efi_m_debug_info_table_header.efi_debug_image_info_table; + + if (efi_m_debug_info_table_header.table_size >= efi_m_max_table_entries) { + /* table is full, re-allocate the buffer increasing the size + * by 4 KiB. + */ + table_size = efi_m_max_table_entries * EFI_DEBUG_TABLE_ENTRY_SIZE; + + ret = efi_realloc((void **)table, table_size + EFI_PAGE_SIZE); + + if (ret != EFI_SUCCESS) { + efi_m_debug_info_table_header.update_status &= + ~EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS; + return ret; + } + + /* Enlarge the max table entries and set the first empty + * entry index to be the original max table entries. + */ + efi_m_max_table_entries += + EFI_PAGE_SIZE / EFI_DEBUG_TABLE_ENTRY_SIZE; + } + + /* We always put the next entry at the end of the currently consumed + * table (i.e. first free entry) + */ + index = efi_m_debug_info_table_header.table_size; + + /* Allocate data for new entry. */ + ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, + sizeof(union efi_debug_image_info), + (void **)(&(*table)[index].normal_image)); + if (ret == EFI_SUCCESS && (*table)[index].normal_image) { + /* Update the entry. */ + (*table)[index].normal_image->image_info_type = image_info_type; + (*table)[index].normal_image->loaded_image_protocol_instance = + loaded_image; + (*table)[index].normal_image->image_handle = image_handle; + + /* Increase the number of EFI_DEBUG_IMAGE_INFO elements and + * set the efi_m_debug_info_table_header in modified status. + */ + efi_m_debug_info_table_header.table_size++; + efi_m_debug_info_table_header.update_status |= + EFI_DEBUG_IMAGE_INFO_TABLE_MODIFIED; + } else { + log_err("Adding new efi_debug_image_info failed\n"); + return ret; + } + + efi_m_debug_info_table_header.update_status &= + ~EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS; + + return EFI_SUCCESS; +} + +/** + * efi_core_remove_debug_image_info_entry() - Remove an efi_debug_image_info entry. + * + * @image_handle: image handle for the image being removed + **/ +void efi_core_remove_debug_image_info_entry(efi_handle_t image_handle) +{ + union efi_debug_image_info *table; + u32 index; + + efi_m_debug_info_table_header.update_status |= + EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS; + + table = efi_m_debug_info_table_header.efi_debug_image_info_table; + + for (index = 0; index < efi_m_max_table_entries; index++) { + if (table[index].normal_image && + table[index].normal_image->image_handle == image_handle) { + /* Found a match. Free up the table entry. + * Move the tail of the table one slot to the front. + */ + efi_free_pool(table[index].normal_image); + + memmove(&table[index], + &table[index + 1], + (efi_m_debug_info_table_header.table_size - + index - 1) * EFI_DEBUG_TABLE_ENTRY_SIZE); + + /* Decrease the number of EFI_DEBUG_IMAGE_INFO + * elements and set the efi_m_debug_info_table_header + * in modified status. + */ + efi_m_debug_info_table_header.table_size--; + table[efi_m_debug_info_table_header.table_size].normal_image = + NULL; + efi_m_debug_info_table_header.update_status |= + EFI_DEBUG_IMAGE_INFO_TABLE_MODIFIED; + break; + } + } + + efi_m_debug_info_table_header.update_status &= + ~EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS; +} -- cgit v1.2.3 From 2e5ca84aad3df9f3d6e1f844a29012d42291b42b Mon Sep 17 00:00:00 2001 From: "Ying-Chun Liu (PaulLiu)" Date: Thu, 3 Jul 2025 07:28:11 +0100 Subject: efi: selftest: add selftest for EFI_DEBUG_SUPPORT Add selftest to check the installed configuration table that has the correct GUID. Cc: Heinrich Schuchardt Cc: Ilias Apalodimas Signed-off-by: Ying-Chun Liu (PaulLiu) Acked-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt Signed-off-by: Ilias Apalodimas --- lib/efi_selftest/Makefile | 2 ++ lib/efi_selftest/efi_selftest_debug_support.c | 40 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 lib/efi_selftest/efi_selftest_debug_support.c (limited to 'lib') diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index d78bf7d6191..842433f68aa 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -78,6 +78,8 @@ endif obj-$(CONFIG_EFI_ESRT) += efi_selftest_esrt.o +obj-$(CONFIG_EFI_DEBUG_SUPPORT) += efi_selftest_debug_support.o + targets += \ efi_miniapp_file_image_exception.h \ efi_miniapp_file_image_exit.h \ diff --git a/lib/efi_selftest/efi_selftest_debug_support.c b/lib/efi_selftest/efi_selftest_debug_support.c new file mode 100644 index 00000000000..9ca8b3f82f5 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_debug_support.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * efi_selftest_debug_support + * + * Copyright (c) 2025 Ying-Chun Liu, Linaro Ltd. + * + * Test the EFI_DEBUG_SUPPORT + */ + +#include +#include + +/** + * efi_st_debug_support_execute() - execute test + * + * Test EFI_DEBUG_SUPPORT tables. + * + * Return: status code + */ +static int efi_st_debug_support_execute(void) +{ + struct efi_debug_image_info_table_header *efi_st_debug_info_table_header = NULL; + efi_guid_t efi_debug_image_info_table_guid = EFI_DEBUG_IMAGE_INFO_TABLE_GUID; + + /* get EFI_DEBUG_IMAGE_INFO_TABLE */ + efi_st_debug_info_table_header = efi_st_get_config_table(&efi_debug_image_info_table_guid); + + if (!efi_st_debug_info_table_header) { + efi_st_error("Missing EFI_DEBUG_IMAGE_INFO_TABLE\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +EFI_UNIT_TEST(debug_support) = { + .name = "debug_support", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .execute = efi_st_debug_support_execute, +}; -- cgit v1.2.3 From 95732f2bf8700f9ec17983b419073b6a16b79aef Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 29 Apr 2025 10:41:38 +0200 Subject: efi_loader: add dbginfodump.efi Provide a test application to dump the EFI_DEBUG_IMAGE_INFO_TABLE as implemented in EDK II. EFI_DEBUG_IMAGE_INFO is not packed in contrast to many other EFI structures. As of today EDK II when removing an entry in the EfiDebugImageInfoTable just sets NormalImage = NULL but does not compact the array. So TableSize reflects the number of non-NULL entries and not the array size as reported independently in https://github.com/tianocore/edk2/pull/11013 and https://github.com/tianocore/edk2/pull/11019. The current implementation tolerates this deviation from the UEFI specification. This is what the output may look like: Debug Info Table Dump ===================== => dump Modified Number of entries: 0x0000004a Info type 0x00000001 Address: [0x000000008315a000, 0x00000000831bafff] File: FvFile(D6A2CB7F-6A18-4E2F-B43B-9920A733700A) Handle: 0x000000017fe3cb18 ... Info type 0x00000001 Address: [0x000000017e8db000, 0x000000017ea00f3f] File: FvFile(7C04A583-9E3E-4F1C-AD65-E05268D0B4D1) Handle: 0x000000017f358e98 Info type 0x00000001 Address: [0x000000017eae5000, 0x000000017eae81ff] File: \dbginfodump.efi Handle: 0x000000017eaf0298 => Signed-off-by: Heinrich Schuchardt Acked-by: Ilias Apalodimas Signed-off-by: Ilias Apalodimas --- lib/efi_loader/Makefile | 1 + lib/efi_loader/dbginfodump.c | 356 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 357 insertions(+) create mode 100644 lib/efi_loader/dbginfodump.c (limited to 'lib') diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 51ccf1cd87e..bfa607c8827 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -21,6 +21,7 @@ ifeq ($(CONFIG_GENERATE_ACPI_TABLE),) apps-y += dtbdump endif apps-$(CONFIG_BOOTEFI_TESTAPP_COMPILE) += testapp +apps-y += dbginfodump obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-$(CONFIG_EFI_BOOTMGR) += efi_bootmgr.o diff --git a/lib/efi_loader/dbginfodump.c b/lib/efi_loader/dbginfodump.c new file mode 100644 index 00000000000..adbbd5060cc --- /dev/null +++ b/lib/efi_loader/dbginfodump.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2025, Heinrich Schuchardt + * + * dbginfodump.efi prints out the content of the EFI_DEBUG_IMAGE_INFO_TABLE. + */ + +#include + +/** + * BUFFER_SIZE - size of the command line input buffer + */ +#define BUFFER_SIZE 64 + +static struct efi_simple_text_output_protocol *cerr; +static struct efi_simple_text_output_protocol *cout; +static struct efi_simple_text_input_protocol *cin; +static efi_handle_t handle; +static struct efi_system_table *systable; +static struct efi_boot_services *bs; + +static efi_guid_t guid_device_path_to_text_protocol = + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; + +static struct efi_device_path_to_text_protocol *device_path_to_text; + +/* EFI_DEBUG_IMAGE_INFO_TABLE_GUID */ +static const efi_guid_t dbg_info_guid = + EFI_GUID(0x49152E77, 0x1ADA, 0x4764, 0xB7, 0xA2, + 0x7A, 0xFE, 0xFE, 0xD9, 0x5E, 0x8B); + +/* EFI_DEBUG_IMAGE_INFO_NORMAL */ +struct dbg_info { + u32 type; + struct efi_loaded_image *info; + efi_handle_t handle; +}; + +/* FI_DEBUG_IMAGE_INFO_TABLE_HEADER */ +struct dbg_info_header { + u32 status; + u32 size; + struct dbg_info **info; +}; + +/** + * print() - print string + * + * @string: text + */ +static void print(u16 *string) +{ + cout->output_string(cout, string); +} + +/** + * error() - print error string + * + * @string: error text + */ +static void error(u16 *string) +{ + cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK); + print(string); + cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); +} + +/** + * printu() - print unsigned + * + * @val: value to print + */ +static void printu(u32 val) +{ + u16 str[19] = u"0x"; + u16 *ptr = &str[2]; + u16 ch; + + for (ssize_t i = 8 * sizeof(u32) - 4; i >= 0; i -= 4) { + ch = (val >> i & 0xf) + '0'; + if (ch > '9') + ch += 'a' - '9' - 1; + *ptr++ = ch; + } + *ptr = 0; + print(str); +} + +/** + * printp() - print pointer + * + * @p: pointer + */ +static void printp(void *p) +{ + u16 str[19] = u"0x"; + u16 *ptr = &str[2]; + u16 ch; + + for (ssize_t i = 8 * sizeof(void *) - 4; i >= 0; i -= 4) { + ch = ((uintptr_t)p >> i & 0xf) + '0'; + if (ch > '9') + ch += 'a' - '9' - 1; + *ptr++ = ch; + } + *ptr = 0; + print(str); +} + +/** + * efi_input() - read string from console + * + * @buffer: input buffer + * @buffer_size: buffer size + * Return: status code + */ +static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) +{ + struct efi_input_key key = {0}; + efi_uintn_t index; + efi_uintn_t pos = 0; + u16 outbuf[2] = u" "; + efi_status_t ret; + + /* Drain the console input */ + ret = cin->reset(cin, true); + *buffer = 0; + for (;;) { + ret = bs->wait_for_event(1, &cin->wait_for_key, &index); + if (ret != EFI_SUCCESS) + continue; + ret = cin->read_key_stroke(cin, &key); + if (ret != EFI_SUCCESS) + continue; + switch (key.scan_code) { + case 0x17: /* Escape */ + print(u"\r\nAborted\r\n"); + return EFI_ABORTED; + default: + break; + } + switch (key.unicode_char) { + case 0x08: /* Backspace */ + if (pos) { + buffer[pos--] = 0; + print(u"\b \b"); + } + break; + case 0x0a: /* Linefeed */ + case 0x0d: /* Carriage return */ + print(u"\r\n"); + return EFI_SUCCESS; + default: + break; + } + /* Ignore surrogate codes */ + if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF) + continue; + if (key.unicode_char >= 0x20 && + pos < buffer_size - 1) { + *outbuf = key.unicode_char; + buffer[pos++] = key.unicode_char; + buffer[pos] = 0; + print(outbuf); + } + } +} + +/** + * skip_whitespace() - skip over leading whitespace + * + * @pos: UTF-16 string + * Return: pointer to first non-whitespace + */ +static u16 *skip_whitespace(u16 *pos) +{ + for (; *pos && *pos <= 0x20; ++pos) + ; + return pos; +} + +/** + * starts_with() - check if @string starts with @keyword + * + * @string: string to search for keyword + * @keyword: keyword to be searched + * Return: true fi @string starts with the keyword + */ +static bool starts_with(u16 *string, u16 *keyword) +{ + for (; *keyword; ++string, ++keyword) { + if (*string != *keyword) + return false; + } + return true; +} + +/** + * do_help() - print help + */ +static void do_help(void) +{ + error(u"dump - print debug info table\r\n"); + error(u"exit - exit the shell\r\n"); +} + +/** + * get_dbg_info_table() - get debug info table + * + * Return: debug info table or NULL + */ +static void *get_dbg_info(void) +{ + void *dbg = NULL; + efi_uintn_t i; + + for (i = 0; i < systable->nr_tables; ++i) { + if (!memcmp(&systable->tables[i].guid, &dbg_info_guid, + sizeof(efi_guid_t))) { + dbg = systable->tables[i].table; + break; + } + } + return dbg; +} + +/** + * print_info() - print loaded image protocol + */ +static void print_info(struct efi_loaded_image *info) +{ + print(u" Address: ["); + printp(info->image_base); + print(u", "); + printp(info->image_base + info->image_size - 1); + print(u"]\r\n"); + if (device_path_to_text && info->file_path) { + u16 *string; + + string = device_path_to_text->convert_device_path_to_text( + info->file_path, true, false); + if (!string) { + error(u"ConvertDevicePathToText failed"); + } else { + print(u" File: "); + print(string); + } + print(u"\r\n"); + } +} + +/** + * do_dump() - print debug info table + */ +static efi_status_t do_dump(void) +{ + struct dbg_info_header *dbg; + u32 count; + + dbg = get_dbg_info(); + if (!dbg) { + error(u"Debug info table not found\r\n"); + return EFI_NOT_FOUND; + } + if (dbg->status & 0x01) { + error(u"Update in progress\r\n"); + return EFI_LOAD_ERROR; + } + if (dbg->status & 0x02) + print(u"Modified\r\n"); + print(u"Number of entries: "); + printu(dbg->size); + print(u"\r\n"); + + count = dbg->size; + for (u32 i = 0; count; ++i) { + struct dbg_info *info = dbg->info[i]; + + /* + * The EDK II implementation decreases the size field and + * writes a NULL value when deleting an entry which is not + * backed by the UEFI specification. + */ + if (!info) { + print(u"Deleted entry\r\n"); + continue; + } + --count; + print(u"Info type "); + printu(info->type); + print(u"\r\n"); + if (info->type != 1) + continue; + print_info(info->info); + print(u" Handle: "); + printp(info->handle); + print(u"\r\n"); + } + + return EFI_SUCCESS; +} + +/** + * efi_main() - entry point of the EFI application. + * + * @handle: handle of the loaded image + * @systab: system table + * Return: status code + */ +efi_status_t EFIAPI efi_main(efi_handle_t image_handle, + struct efi_system_table *systab) +{ + efi_status_t ret; + + handle = image_handle; + systable = systab; + cerr = systable->std_err; + cout = systable->con_out; + cin = systable->con_in; + bs = systable->boottime; + + cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); + cout->clear_screen(cout); + cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK); + print(u"Debug Info Table Dump\r\n=====================\r\n\r\n"); + cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); + + ret = bs->locate_protocol(&guid_device_path_to_text_protocol, + NULL, (void **)&device_path_to_text); + if (ret != EFI_SUCCESS) { + error(u"No device path to text protocol\r\n"); + device_path_to_text = NULL; + } + + for (;;) { + u16 command[BUFFER_SIZE]; + u16 *pos; + efi_uintn_t ret; + + print(u"=> "); + ret = efi_input(command, sizeof(command)); + if (ret == EFI_ABORTED) + break; + pos = skip_whitespace(command); + if (starts_with(pos, u"exit")) + break; + else if (starts_with(pos, u"dump")) + do_dump(); + else + do_help(); + } + + cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); + cout->clear_screen(cout); + return EFI_SUCCESS; +} -- cgit v1.2.3