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/efi_loader') 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.3.1 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/efi_loader') 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.3.1 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/efi_loader') 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.3.1 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/efi_loader') 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.3.1 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 --- include/efi_api.h | 16 +++++++++++++++ include/efi_loader.h | 2 ++ 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 +++++++ 6 files changed, 75 insertions(+) create mode 100644 lib/efi_loader/efi_debug_support.c (limited to 'lib/efi_loader') diff --git a/include/efi_api.h b/include/efi_api.h index eb61eafa028..6c4c1a0cc7b 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -259,6 +259,22 @@ struct efi_capsule_result_variable_header { efi_status_t capsule_status; } __packed; +/** + * struct efi_system_table_pointer - struct to store the pointer of system + * table. + * @signature: The signature of this struct. + * @efi_system_table_base: The physical address of System Table. + * @crc32: CRC32 checksum + * + * This struct is design for hardware debugger to search through memory to + * get the address of EFI System Table. + */ +struct efi_system_table_pointer { + u64 signature; + efi_physical_addr_t efi_system_table_base; + u32 crc32; +}; + struct efi_memory_range { efi_physical_addr_t address; u64 length; diff --git a/include/efi_loader.h b/include/efi_loader.h index 8fd09aad2d0..ada29a5bad3 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -643,6 +643,8 @@ efi_status_t efi_tcg2_measure_dtb(void *dtb); efi_status_t efi_root_node_register(void); /* Called by bootefi to initialize runtime */ efi_status_t efi_initialize_system_table(void); +/* Called by bootefi to initialize debug */ +efi_status_t efi_initialize_system_table_pointer(void); /* efi_runtime_detach() - detach unimplemented runtime functions */ void efi_runtime_detach(void); /* efi_convert_pointer() - convert pointer to virtual address */ 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.3.1 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 --- include/efi_api.h | 53 ++++++++++++++++++++++++++++++++++++++ include/efi_loader.h | 2 ++ lib/efi_loader/efi_debug_support.c | 6 +++++ lib/efi_loader/efi_setup.c | 11 ++++++++ 4 files changed, 72 insertions(+) (limited to 'lib/efi_loader') diff --git a/include/efi_api.h b/include/efi_api.h index 6c4c1a0cc7b..8da0a350ce3 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -238,6 +238,10 @@ enum efi_reset_type { EFI_GUID(0xcce33c35, 0x74ac, 0x4087, 0xbc, 0xe7, \ 0x8b, 0x29, 0xb0, 0x2e, 0xeb, 0x27) +#define EFI_DEBUG_IMAGE_INFO_TABLE_GUID \ + EFI_GUID(0x49152e77, 0x1ada, 0x4764, 0xb7, 0xa2, \ + 0x7a, 0xfe, 0xfe, 0xd9, 0x5e, 0x8b) + struct efi_conformance_profiles_table { u16 version; u16 number_of_profiles; @@ -574,6 +578,55 @@ struct efi_loaded_image { efi_status_t (EFIAPI *unload)(efi_handle_t image_handle); }; +#define EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS 0x01 +#define EFI_DEBUG_IMAGE_INFO_TABLE_MODIFIED 0x02 + +/** + * struct efi_debug_image_info_normal - Store Debug Information for normal + * image. + * @image_info_type: the type of image info. + * @loaded_image_protocol_instance: the pointer to struct efi_loaded_image. + * @image_handle: the EFI handle of the image. + * + * This struct is created by efi_load_image() and store the information + * for debugging an normal image. + */ +struct efi_debug_image_info_normal { + u32 image_info_type; + struct efi_loaded_image *loaded_image_protocol_instance; + efi_handle_t image_handle; +}; + +/** + * union efi_debug_image_info - The union to store a pointer for EFI + * DEBUG IMAGE INFO. + * @image_info_type: the type of the image_info if it is not a normal image. + * @normal_image: The pointer to a normal image. + * + * This union is for a pointer that can point to the struct of normal_image. + * Or it points to an image_info_type. + */ +union efi_debug_image_info { + u32 *image_info_type; + struct efi_debug_image_info_normal *normal_image; +}; + +/** + * struct efi_debug_image_info_table_header - store the array of + * struct efi_debug_image_info. + * @update_status: Status to notify this struct is ready to use or not. + * @table_size: The number of elements of efi_debug_image_info_table. + * @efi_debug_image_info_table: The array of efi_debug_image_info. + * + * This struct stores the array of efi_debug_image_info. The + * number of elements is table_size. + */ +struct efi_debug_image_info_table_header { + volatile u32 update_status; + u32 table_size; + union efi_debug_image_info *efi_debug_image_info_table; +}; + #define EFI_DEVICE_PATH_PROTOCOL_GUID \ EFI_GUID(0x09576e91, 0x6d3f, 0x11d2, \ 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) diff --git a/include/efi_loader.h b/include/efi_loader.h index ada29a5bad3..c02bc1b7ef4 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -312,6 +312,8 @@ extern const struct efi_hii_config_routing_protocol efi_hii_config_routing; extern const struct efi_hii_config_access_protocol efi_hii_config_access; extern const struct efi_hii_database_protocol efi_hii_database; extern const struct efi_hii_string_protocol efi_hii_string; +/* structure for EFI_DEBUG_SUPPORT_PROTOCOL */ +extern struct efi_debug_image_info_table_header efi_m_debug_info_table_header; uint16_t *efi_dp_str(struct efi_device_path *dp); 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.3.1 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 --- include/efi_loader.h | 10 ++++++++ lib/efi_loader/efi_memory.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) (limited to 'lib/efi_loader') diff --git a/include/efi_loader.h b/include/efi_loader.h index c02bc1b7ef4..b60ea58d71f 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -875,6 +875,16 @@ efi_status_t efi_next_variable_name(efi_uintn_t *size, u16 **buf, #define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT) /* Allocate boot service data pool memory */ void *efi_alloc(size_t len); +/** + * 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 the buffer + * @size: number of bytes to allocate + * Return: EFI status to indicate success or not + */ +efi_status_t efi_realloc(void **ptr, size_t len); /* Allocate pages on the specified alignment */ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align); /* More specific EFI memory allocator, called by EFI payloads */ 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.3.1 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 --- include/efi_api.h | 2 + include/efi_loader.h | 5 ++ lib/efi_loader/efi_boottime.c | 7 ++ lib/efi_loader/efi_debug_support.c | 137 +++++++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+) (limited to 'lib/efi_loader') diff --git a/include/efi_api.h b/include/efi_api.h index 8da0a350ce3..77a05f752e5 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -581,6 +581,8 @@ struct efi_loaded_image { #define EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS 0x01 #define EFI_DEBUG_IMAGE_INFO_TABLE_MODIFIED 0x02 +#define EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL 0x01 + /** * struct efi_debug_image_info_normal - Store Debug Information for normal * image. diff --git a/include/efi_loader.h b/include/efi_loader.h index b60ea58d71f..3e70ac07055 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -647,6 +647,11 @@ efi_status_t efi_root_node_register(void); efi_status_t efi_initialize_system_table(void); /* Called by bootefi to initialize debug */ efi_status_t efi_initialize_system_table_pointer(void); +/* Called by efi_load_image for register debug info */ +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); +void efi_core_remove_debug_image_info_entry(efi_handle_t image_handle); /* efi_runtime_detach() - detach unimplemented runtime functions */ void efi_runtime_detach(void); /* efi_convert_pointer() - convert pointer to virtual address */ 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.3.1 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/efi_loader') 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.3.1