The EFI Debug Support Table provides an external debugger enough information to determine loaded image information in a quiescent manner.
Add the necessary support to barebox, so the EFI boot time which will be added in the follow-up commit can make use of it. Signed-off-by: Ahmad Fatoum <[email protected]> --- efi/loader/Kconfig | 8 ++ efi/loader/Makefile | 1 + efi/loader/debug_support.c | 192 +++++++++++++++++++++++++++++++++++++ include/efi/loader.h | 2 + include/efi/loader/debug.h | 73 ++++++++++++++ include/efi/services.h | 16 ++++ 6 files changed, 292 insertions(+) create mode 100644 efi/loader/debug_support.c create mode 100644 include/efi/loader/debug.h diff --git a/efi/loader/Kconfig b/efi/loader/Kconfig index e7080d8d5093..43a80869cf8b 100644 --- a/efi/loader/Kconfig +++ b/efi/loader/Kconfig @@ -1,3 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only # SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/a0fe8cedcbe8c76403a77e57eac228b8f778a3ae/lib/efi_loader/Kconfig +config EFI_LOADER_DEBUG_SUPPORT + bool "EFI Debug Support" + default y + 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. diff --git a/efi/loader/Makefile b/efi/loader/Makefile index c635b5b6a533..593d456f0d39 100644 --- a/efi/loader/Makefile +++ b/efi/loader/Makefile @@ -4,3 +4,4 @@ obj-y += memory.o pool_alloc.o obj-y += trace.o obj-y += table.o obj-y += devicepath.o +obj-y += debug_support.o diff --git a/efi/loader/debug_support.c b/efi/loader/debug_support.c new file mode 100644 index 000000000000..8b5ee574bab6 --- /dev/null +++ b/efi/loader/debug_support.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/7e2506e3a247c787a9ad4b4db00bc8e6a348df90/lib/efi_loader/efi_debug_support.c +/* + * EFI debug support + * + * Copyright (c) 2025 Ying-Chun Liu, Linaro Ltd. <[email protected]> + */ + +#define pr_fmt(fmt) "efi-loader: debug: " fmt + +#include <linux/printk.h> +#include <efi/loader.h> +#include <efi/loader/debug.h> +#include <efi/services.h> +#include <efi/error.h> +#include <linux/sizes.h> +#include <crc.h> + +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_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 + * + * 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, "debug"); + + if (!systab_pointer) { + pr_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; +} + +/** + * 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, + "debug_image"); + + 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), + "debug_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 { + pr_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; +} diff --git a/include/efi/loader.h b/include/efi/loader.h index 47fdcf5c5114..f2ca32c578a2 100644 --- a/include/efi/loader.h +++ b/include/efi/loader.h @@ -19,6 +19,8 @@ struct efi_table_hdr; /* Key identifying current memory map */ extern efi_uintn_t efi_memory_map_key; +extern struct efi_system_table systab; + /* Allocate boot service data pool memory */ void *efi_alloc(size_t len, const char *name); /* Reallocate boot service data pool memory */ diff --git a/include/efi/loader/debug.h b/include/efi/loader/debug.h new file mode 100644 index 000000000000..7b810c2218a7 --- /dev/null +++ b/include/efi/loader/debug.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-SnippetCopyrightText: 2016 Alexander Graf */ + +#ifndef _EFI_LOADER_DEBUG_H +#define _EFI_LOADER_DEBUG_H + +#include <efi/types.h> + +struct efi_loaded_image; + +/* 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); + +#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. + * @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; +}; + +/* structure for EFI_DEBUG_SUPPORT_PROTOCOL */ +extern struct efi_debug_image_info_table_header efi_m_debug_info_table_header; + +#endif diff --git a/include/efi/services.h b/include/efi/services.h index c0dbac8743cc..f6fd268b2923 100644 --- a/include/efi/services.h +++ b/include/efi/services.h @@ -259,6 +259,22 @@ struct efi_loaded_image { efi_status_t (EFIAPI *unload)(efi_handle_t image_handle); }; +/** + * 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; +}; + enum efi_locate_search_type; int __efi_locate_handle(struct efi_boot_services *bs, -- 2.47.3
