On Tue Apr 29, 2025 at 11:41 AM EEST, Heinrich Schuchardt wrote: > 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 <heinrich.schucha...@canonical.com> > --- > v2: > Tolerate EDK II setting NormalImage = NULL for deleted entries. > Update commit message. > Add more code comments. > --- > lib/efi_loader/Makefile | 1 + > lib/efi_loader/dbginfodump.c | 356 +++++++++++++++++++++++++++++++++++ > 2 files changed, 357 insertions(+) > create mode 100644 lib/efi_loader/dbginfodump.c > > diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile > index cf050e5385d..0ff068b4307 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 <xypron.g...@gmx.de> > + * > + * dbginfodump.efi prints out the content of the EFI_DEBUG_IMAGE_INFO_TABLE. > + */ > + > +#include <efi_api.h> > + > +/** > + * 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; > +}
Acked-by: Ilias Apalodimas <ilias.apalodi...@linaro.org>