之前提到过,UEFI 上没有 E820 ,而是通过gBS->GetMemoryMap() 来获得当前内存分配情况,这里给出了一个驱动代码,可以在UEFI Shell 下加载,他会替换之前的GetMemoryMap() 函数,基于之前的返回值增加一个条目:0xFED00000-0xFED1FFFF ,报告为 available 。
HookMemoryMapDxe.c
/** @file
Memory Map Hook DXE Driver
Hooks GetMemoryMap to add a fake memory region
**/
#include <Uefi.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
// Hook Protocol GUID
#define MEMORY_MAP_HOOK_PROTOCOL_GUID \
{ 0x87654321, 0x4321, 0x8765, { 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90 } }
EFI_GUID gMemoryMapHookProtocolGuid = MEMORY_MAP_HOOK_PROTOCOL_GUID;
// Hook数据结构
typedef struct {
UINT32 Signature;
EFI_GET_MEMORY_MAP OriginalGetMemoryMap;
EFI_PHYSICAL_ADDRESS FakeMemoryStart;
UINT64 FakeMemoryPages;
BOOLEAN HookEnabled;
} MEMORY_MAP_HOOK_DATA;
#define HOOK_SIGNATURE SIGNATURE_32('H','M','A','P')
// 全局变量
STATIC MEMORY_MAP_HOOK_DATA *gHookData = NULL;
STATIC EFI_HANDLE gProtocolHandle = NULL;
STATIC EFI_EVENT gExitBootServicesEvent = NULL;
/**
Hooked GetMemoryMap function
**/
EFI_STATUS
EFIAPI
HookedGetMemoryMap (
IN OUT UINTN *MemoryMapSize,
IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
OUT UINTN *MapKey,
OUT UINTN *DescriptorSize,
OUT UINT32 *DescriptorVersion
)
{
EFI_STATUS Status;
UINTN OriginalMapSize;
EFI_MEMORY_DESCRIPTOR *OriginalMap;
UINTN NewMapSize;
UINTN EntryCount;
UINTN Index;
BOOLEAN NeedToAddEntry = TRUE;
// 检查Hook是否启用
if (gHookData == NULL || !gHookData->HookEnabled || gHookData->OriginalGetMemoryMap == NULL) {
return EFI_UNSUPPORTED;
}
// 如果MemoryMap为NULL,只是查询大小
if (MemoryMap == NULL) {
Status = gHookData->OriginalGetMemoryMap(MemoryMapSize, MemoryMap, MapKey, DescriptorSize, DescriptorVersion);
if (Status == EFI_BUFFER_TOO_SMALL) {
// 为新增的条目预留空间
*MemoryMapSize += *DescriptorSize;
}
return Status;
}
// 分配临时缓冲区
OriginalMapSize = *MemoryMapSize + *DescriptorSize;
OriginalMap = AllocatePool(OriginalMapSize);
if (OriginalMap == NULL) {
return EFI_OUT_OF_RESOURCES;
}
// 调用原始的GetMemoryMap
Status = gHookData->OriginalGetMemoryMap(&OriginalMapSize, OriginalMap, MapKey, DescriptorSize, DescriptorVersion);
if (EFI_ERROR(Status)) {
FreePool(OriginalMap);
return Status;
}
EntryCount = OriginalMapSize / *DescriptorSize;
// 检查是否已经存在重叠的内存区域
for (Index = 0; Index < EntryCount; Index++) {
EFI_MEMORY_DESCRIPTOR *Entry = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)OriginalMap + Index * (*DescriptorSize));
EFI_PHYSICAL_ADDRESS EntryStart = Entry->PhysicalStart;
EFI_PHYSICAL_ADDRESS EntryEnd = EntryStart + (Entry->NumberOfPages * EFI_PAGE_SIZE) - 1;
// 检查是否与新条目重叠
if ((gHookData->FakeMemoryStart >= EntryStart && gHookData->FakeMemoryStart <= EntryEnd) ||
(gHookData->FakeMemoryStart + (gHookData->FakeMemoryPages * EFI_PAGE_SIZE) - 1 >= EntryStart &&
gHookData->FakeMemoryStart + (gHookData->FakeMemoryPages * EFI_PAGE_SIZE) - 1 <= EntryEnd)) {
NeedToAddEntry = FALSE;
break;
}
}
// 计算新的内存映射大小
if (NeedToAddEntry) {
NewMapSize = OriginalMapSize + *DescriptorSize;
} else {
NewMapSize = OriginalMapSize;
}
// 检查提供的缓冲区是否足够大
if (*MemoryMapSize < NewMapSize) {
*MemoryMapSize = NewMapSize;
FreePool(OriginalMap);
return EFI_BUFFER_TOO_SMALL;
}
// 复制原始内存映射
CopyMem(MemoryMap, OriginalMap, OriginalMapSize);
// 如果需要,添加新的内存条目
if (NeedToAddEntry) {
EFI_MEMORY_DESCRIPTOR *NewEntry = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + OriginalMapSize);
NewEntry->Type = EfiConventionalMemory; // Available memory
NewEntry->PhysicalStart = gHookData->FakeMemoryStart;
NewEntry->VirtualStart = 0;
NewEntry->NumberOfPages = gHookData->FakeMemoryPages;
NewEntry->Attribute = EFI_MEMORY_WB; // Write-back cacheable
}
*MemoryMapSize = NewMapSize;
FreePool(OriginalMap);
return EFI_SUCCESS;
}
/**
ExitBootServices event handler
**/
VOID
EFIAPI
ExitBootServicesNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
// 在ExitBootServices之前恢复原始函数指针
if (gHookData != NULL && gHookData->HookEnabled && gHookData->OriginalGetMemoryMap != NULL) {
gBS->GetMemoryMap = gHookData->OriginalGetMemoryMap;
// 更新CRC32
gBS->Hdr.CRC32 = 0;
gBS->CalculateCrc32((UINT8 *)gBS, gBS->Hdr.HeaderSize, &gBS->Hdr.CRC32);
gHookData->HookEnabled = FALSE;
}
}
/**
Driver entry point
@param ImageHandle The image handle
@param SystemTable The system table
@retval EFI_SUCCESS Driver loaded successfully
**/
EFI_STATUS
EFIAPI
HookMemoryMapDxeEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
DEBUG((DEBUG_INFO, "HookMemoryMapDxe: Driver starting...\n"));
// 分配Hook数据结构
gHookData = AllocateRuntimeZeroPool(sizeof(MEMORY_MAP_HOOK_DATA));
if (gHookData == NULL) {
DEBUG((DEBUG_ERROR, "HookMemoryMapDxe: Failed to allocate hook data\n"));
return EFI_OUT_OF_RESOURCES;
}
// 初始化Hook数据
gHookData->Signature = HOOK_SIGNATURE;
gHookData->OriginalGetMemoryMap = gBS->GetMemoryMap;
gHookData->FakeMemoryStart = 0xFED00000; // 0xFED00000-0xFED1FFFF
gHookData->FakeMemoryPages = 0x20000 / EFI_PAGE_SIZE; // 128KB = 32 pages
gHookData->HookEnabled = FALSE;
// 安装Protocol (用于标识和查找)
Status = gBS->InstallProtocolInterface(
&gProtocolHandle,
&gMemoryMapHookProtocolGuid,
EFI_NATIVE_INTERFACE,
gHookData
);
if (EFI_ERROR(Status)) {
DEBUG((DEBUG_ERROR, "HookMemoryMapDxe: Failed to install protocol: %r\n", Status));
FreePool(gHookData);
gHookData = NULL;
return Status;
}
// 创建ExitBootServices事件
Status = gBS->CreateEvent(
EVT_SIGNAL_EXIT_BOOT_SERVICES,
TPL_NOTIFY,
ExitBootServicesNotify,
NULL,
&gExitBootServicesEvent
);
if (EFI_ERROR(Status)) {
DEBUG((DEBUG_WARN, "HookMemoryMapDxe: Failed to create ExitBootServices event: %r\n", Status));
// 不是致命错误,继续执行
}
// 安装Hook
gBS->GetMemoryMap = HookedGetMemoryMap;
// 更新CRC32
gBS->Hdr.CRC32 = 0;
gBS->CalculateCrc32((UINT8 *)gBS, gBS->Hdr.HeaderSize, &gBS->Hdr.CRC32);
// 启用Hook
gHookData->HookEnabled = TRUE;
DEBUG((DEBUG_INFO, "HookMemoryMapDxe: Driver loaded successfully\n"));
DEBUG((DEBUG_INFO, "HookMemoryMapDxe: Hook installed and enabled\n"));
DEBUG((DEBUG_INFO, "HookMemoryMapDxe: Fake memory region: 0x%lx-0x%lx\n",
gHookData->FakeMemoryStart,
gHookData->FakeMemoryStart + (gHookData->FakeMemoryPages * EFI_PAGE_SIZE) - 1));
return EFI_SUCCESS;
}
HookMemoryMapDxe.inf
这个代码需要放置在MdeModulePkg下面,然后在MdeModulePkg.dsc添加:
[PcdsDynamicExDefault]
gEfiMdeModulePkgTokenSpaceGuid.PcdRecoveryFileName|L"FVMAIN.FV"
[Components]
MdeModulePkg/HookMemoryMapDxe/HookMemoryMapDxe.inf
MdeModulePkg/Application/HelloWorld/HelloWorld.inf
MdeModulePkg/Application/DumpDynPcd/DumpDynPcd.inf
MdeModulePkg/Application/MemoryProfileInfo/MemoryProfileInfo.inf
之后,使用下面的命令进行编译即可:
build -a X64 -p MdeModulePkg/MdeModulePkg.dsc -t VS2019
完整的代码下载:
使用时,只需要在 UEFI Shell 下 load HookMemoryMapDxe.efi 即可,之后可以使用 memmap 命令看到改动结果。
特别注意:这里只是一个Demo ,实际使用时需要根据你的需求进行修改。