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 <heinrich.schuchardt@canonical.com> Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
This commit is contained in:
committed by
Ilias Apalodimas
parent
2e5ca84aad
commit
95732f2bf8
@@ -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
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright 2025, Heinrich Schuchardt <xypron.glpk@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;
|
||||
}
|
||||
Reference in New Issue
Block a user