lib: uefi: add EfiDebugImageInfo for debug
This commit adds the functionality of generate EfiDebugImageInfo 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. Signed-off-by: Ying-Chun Liu (PaulLiu) <paulliu@debian.org>
This commit is contained in:
committed by
Kelvin Zhang
parent
1ffb54dace
commit
c5612330cc
@@ -23,8 +23,19 @@
|
||||
#include <uefi/boot_service.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
#include "boot_service_provider.h"
|
||||
#include "memory_protocols.h"
|
||||
|
||||
struct EFI_DEVICE_PATH_FILE_PATH_PROTOCOL {
|
||||
struct EFI_DEVICE_PATH_PROTOCOL dp;
|
||||
uint16_t str[];
|
||||
};
|
||||
|
||||
static constexpr size_t EFI_DEVICE_PATH_TYPE_END = 0x7f;
|
||||
static constexpr size_t EFI_DEVICE_PATH_SUB_TYPE_END = 0xff;
|
||||
static constexpr size_t EFI_DEVICE_PATH_TYPE_MEDIA_DEVICE = 0x4;
|
||||
static constexpr size_t EFI_DEVICE_PATH_SUB_TYPE_FILE_PATH = 0x4;
|
||||
|
||||
namespace {
|
||||
struct EfiSystemTablePointer *efi_systab_pointer = nullptr;
|
||||
}
|
||||
@@ -61,3 +72,191 @@ EfiStatus efi_initialize_system_table_pointer(struct EfiSystemTable *system_tabl
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static uint32_t efi_m_max_table_entries;
|
||||
|
||||
static constexpr size_t EFI_DEBUG_TABLE_ENTRY_SIZE = (sizeof(union EfiDebugImageInfo));
|
||||
|
||||
EfiStatus efi_core_new_debug_image_info_entry(uint32_t image_info_type,
|
||||
struct EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
|
||||
EfiHandle image_handle) {
|
||||
/* 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;
|
||||
|
||||
union EfiDebugImageInfo *table;
|
||||
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.
|
||||
*/
|
||||
uint32_t table_size = efi_m_max_table_entries * EFI_DEBUG_TABLE_ENTRY_SIZE;
|
||||
union EfiDebugImageInfo *new_table;
|
||||
allocate_pool(BOOT_SERVICES_DATA,
|
||||
table_size + PAGE_SIZE,
|
||||
reinterpret_cast<void **>(&new_table));
|
||||
|
||||
if (!new_table) {
|
||||
efi_m_debug_info_table_header.update_status &=
|
||||
~EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
|
||||
return OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
memset(new_table, 0, table_size + PAGE_SIZE);
|
||||
|
||||
/* Copy the old table into the new one. */
|
||||
memcpy(new_table, table, table_size);
|
||||
/* Free the old table. */
|
||||
free_pool(table);
|
||||
/* Update the table header. */
|
||||
table = new_table;
|
||||
efi_m_debug_info_table_header.efi_debug_image_info_table =
|
||||
new_table;
|
||||
|
||||
/* Enlarge the max table entries and set the first empty
|
||||
* entry index to be the original max table entries.
|
||||
*/
|
||||
efi_m_max_table_entries +=
|
||||
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)
|
||||
*/
|
||||
uint32_t index = efi_m_debug_info_table_header.table_size;
|
||||
|
||||
/* Allocate data for new entry. */
|
||||
allocate_pool(BOOT_SERVICES_DATA,
|
||||
sizeof(union EfiDebugImageInfo),
|
||||
reinterpret_cast<void **>(&table[index].normal_image));
|
||||
if (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 {
|
||||
return OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
efi_m_debug_info_table_header.update_status &=
|
||||
~EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
void efi_core_remove_debug_image_info_entry(EfiHandle image_handle)
|
||||
{
|
||||
efi_m_debug_info_table_header.update_status |=
|
||||
EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
|
||||
|
||||
union EfiDebugImageInfo *table;
|
||||
table = efi_m_debug_info_table_header.efi_debug_image_info_table;
|
||||
|
||||
for (uint32_t 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.
|
||||
*/
|
||||
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 =
|
||||
nullptr;
|
||||
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;
|
||||
}
|
||||
|
||||
EfiStatus setup_debug_support(EfiSystemTable &table,
|
||||
char *image_base,
|
||||
size_t virtual_size,
|
||||
bdev_t *dev) {
|
||||
struct EFI_LOADED_IMAGE_PROTOCOL *efiLoadedImageProtocol = nullptr;
|
||||
|
||||
allocate_pool(BOOT_SERVICES_DATA,
|
||||
sizeof(struct EFI_LOADED_IMAGE_PROTOCOL),
|
||||
reinterpret_cast<void **>(&efiLoadedImageProtocol));
|
||||
memset(efiLoadedImageProtocol, 0, sizeof(struct EFI_LOADED_IMAGE_PROTOCOL));
|
||||
|
||||
efiLoadedImageProtocol->Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION;
|
||||
efiLoadedImageProtocol->SystemTable = &table;
|
||||
efiLoadedImageProtocol->ImageBase = reinterpret_cast<void *>(image_base);
|
||||
efiLoadedImageProtocol->ImageSize = virtual_size;
|
||||
char *device_buf = nullptr;
|
||||
size_t fpsize = sizeof(struct EFI_DEVICE_PATH_PROTOCOL) + 2 * (strlen(dev->name) + 2);
|
||||
allocate_pool(BOOT_SERVICES_DATA,
|
||||
fpsize + sizeof(struct EFI_DEVICE_PATH_PROTOCOL),
|
||||
reinterpret_cast<void **>(&device_buf));
|
||||
memset(device_buf, 0, fpsize + sizeof(struct EFI_DEVICE_PATH_PROTOCOL));
|
||||
struct EFI_DEVICE_PATH_FILE_PATH_PROTOCOL *fp = reinterpret_cast<struct EFI_DEVICE_PATH_FILE_PATH_PROTOCOL *>(device_buf);
|
||||
struct EFI_DEVICE_PATH_PROTOCOL *dp_end = reinterpret_cast<struct EFI_DEVICE_PATH_PROTOCOL *>(device_buf + fpsize);
|
||||
fp->dp.Type = EFI_DEVICE_PATH_TYPE_MEDIA_DEVICE;
|
||||
fp->dp.SubType = EFI_DEVICE_PATH_SUB_TYPE_FILE_PATH;
|
||||
fp->dp.Length[0] = fpsize % 256;
|
||||
fp->dp.Length[1] = fpsize / 256;
|
||||
fp->str[0] = '\\';
|
||||
for (size_t i = 0; i < strlen(dev->name); i++) {
|
||||
fp->str[i+1] = dev->name[i];
|
||||
}
|
||||
dp_end->Type = EFI_DEVICE_PATH_TYPE_END;
|
||||
dp_end->SubType = EFI_DEVICE_PATH_SUB_TYPE_END;
|
||||
dp_end->Length[0] = sizeof(struct EFI_DEVICE_PATH_PROTOCOL) % 256;
|
||||
dp_end->Length[1] = sizeof(struct EFI_DEVICE_PATH_PROTOCOL) / 256;
|
||||
efiLoadedImageProtocol->FilePath = reinterpret_cast<struct EFI_DEVICE_PATH_PROTOCOL *>(device_buf);
|
||||
return efi_core_new_debug_image_info_entry(EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL,
|
||||
efiLoadedImageProtocol,
|
||||
image_base);
|
||||
}
|
||||
|
||||
void teardown_debug_support(char *image_base) {
|
||||
char *device_buf = nullptr;
|
||||
struct EFI_LOADED_IMAGE_PROTOCOL *efiLoadedImageProtocol = nullptr;
|
||||
|
||||
union EfiDebugImageInfo *table;
|
||||
table = efi_m_debug_info_table_header.efi_debug_image_info_table;
|
||||
|
||||
for (uint32_t index = 0; index < efi_m_debug_info_table_header.table_size; index++) {
|
||||
if (table[index].normal_image &&
|
||||
table[index].normal_image->image_handle == image_base) {
|
||||
/* Found a match. Get device_buf and efiLoadedImageProtocol.
|
||||
*/
|
||||
efiLoadedImageProtocol = table[index].normal_image->loaded_image_protocol_instance;
|
||||
device_buf = reinterpret_cast<char *>(efiLoadedImageProtocol->FilePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
efi_core_remove_debug_image_info_entry(image_base);
|
||||
if (device_buf) {
|
||||
free_pool(device_buf);
|
||||
}
|
||||
if (efiLoadedImageProtocol) {
|
||||
free_pool(efiLoadedImageProtocol);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
#ifndef __DEBUG_SUPPORT_
|
||||
#define __DEBUG_SUPPORT_
|
||||
|
||||
#include <lib/bio.h>
|
||||
#include <uefi/system_table.h>
|
||||
#include <uefi/types.h>
|
||||
#include <uefi/protocols/loaded_image_protocol.h>
|
||||
|
||||
static constexpr auto EFI_DEBUG_IMAGE_INFO_TABLE_GUID =
|
||||
EfiGuid{0x49152e77,
|
||||
@@ -31,6 +31,8 @@ static constexpr auto EFI_DEBUG_IMAGE_INFO_TABLE_GUID =
|
||||
static constexpr size_t EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS = 0x01;
|
||||
static constexpr size_t EFI_DEBUG_IMAGE_INFO_TABLE_MODIFIED = 0x02;
|
||||
|
||||
static constexpr size_t EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL = 0x01;
|
||||
|
||||
struct EfiSystemTablePointer {
|
||||
uint64_t signature;
|
||||
EfiSystemTable *system_table_base;
|
||||
@@ -39,7 +41,7 @@ struct EfiSystemTablePointer {
|
||||
|
||||
struct EfiDebugImageInfoNormal {
|
||||
uint32_t image_info_type;
|
||||
EfiLoadedImageProtocol *loaded_image_protocol_instance;
|
||||
struct EFI_LOADED_IMAGE_PROTOCOL *loaded_image_protocol_instance;
|
||||
EfiHandle image_handle;
|
||||
};
|
||||
|
||||
@@ -55,6 +57,16 @@ struct EfiDebugImageInfoTableHeader {
|
||||
};
|
||||
|
||||
EfiStatus efi_initialize_system_table_pointer(struct EfiSystemTable *system_table);
|
||||
EfiStatus efi_core_new_debug_image_info_entry(uint32_t image_info_type,
|
||||
struct EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
|
||||
EfiHandle image_handle);
|
||||
void efi_core_remove_debug_image_info_entry(EfiHandle image_handle);
|
||||
EfiStatus setup_debug_support(EfiSystemTable &table,
|
||||
char *image_base,
|
||||
size_t virtual_size,
|
||||
bdev_t *dev);
|
||||
|
||||
void teardown_debug_support(char *image_base);
|
||||
|
||||
extern struct EfiDebugImageInfoTableHeader efi_m_debug_info_table_header;
|
||||
|
||||
|
||||
@@ -143,6 +143,7 @@ int load_sections_and_execute(bdev_t *dev,
|
||||
printf("efi_initialize_system_table_pointer failed: %lu\n", status);
|
||||
return -static_cast<int>(status);
|
||||
}
|
||||
setup_debug_support(table, image_base, virtual_size, dev);
|
||||
|
||||
constexpr size_t kStackSize = 1 * 1024ul * 1024;
|
||||
auto stack = reinterpret_cast<char *>(alloc_page(kStackSize, 23));
|
||||
@@ -152,8 +153,12 @@ int load_sections_and_execute(bdev_t *dev,
|
||||
stack = nullptr;
|
||||
};
|
||||
printf("Calling kernel with stack [%p, %p]\n", stack, stack + kStackSize - 1);
|
||||
return static_cast<int>(
|
||||
int ret = static_cast<int>(
|
||||
call_with_stack(stack + kStackSize, entry, image_base, &table));
|
||||
|
||||
teardown_debug_support(image_base);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cmd_uefi_load(int argc, const console_cmd_args *argv) {
|
||||
|
||||
Reference in New Issue
Block a user