3. 使用UEFI内存分配服务
任务目标
编写一个UEFI应用程序,调用gBS->AllocatePages()分配一段内存,打印其类型与起始地址,再调用gBS->FreePages()释放,并验证释放结果。
环境与依赖
- EDK2开发环境(OvmfPkg/Qemu或真实固件均可)
- 仅依赖标准UEFI协议:
gBS(Boot Services)、gST->ConOut(控制台输出)
核心实现
以下代码完整实现内存分配、打印与释放逻辑,可直接编译为UEFI应用程序。
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/PrintLib.h>
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS MemoryAddr = 0;
UINTN Pages = 1; // 分配1页(4KB)
CHAR16 TypeName[32];
// 分配内存:类型为 EfiLoaderData(加载器数据)
Status = gBS->AllocatePages (
AllocateAnyPages,
EfiLoaderData,
Pages,
&MemoryAddr
);
if (EFI_ERROR (Status)) {
Print (L"AllocatePages failed: %r\n", Status);
return Status;
}
// 将内存类型枚举值转换为可读字符串
switch (MemoryType) {
case EfiLoaderCode: StrCpyS (TypeName, 32, L"EfiLoaderCode"); break;
case EfiLoaderData: StrCpyS (TypeName, 32, L"EfiLoaderData"); break;
case EfiBootServicesCode: StrCpyS (TypeName, 32, L"EfiBootServicesCode"); break;
case EfiBootServicesData: StrCpyS (TypeName, 32, L"EfiBootServicesData"); break;
case EfiRuntimeServicesCode: StrCpyS (TypeName, 32, L"EfiRuntimeServicesCode"); break;
case EfiRuntimeServicesData: StrCpyS (TypeName, 32, L"EfiRuntimeServicesData"); break;
case EfiConventionalMemory: StrCpyS (TypeName, 32, L"EfiConventionalMemory"); break;
default: StrCpyS (TypeName, 32, L"Unknown"); break;
}
Print (L"Allocated %d page(s)\n", Pages);
Print (L"Memory Type: %s\n", TypeName);
Print (L"Start Address: 0x%lx\n", MemoryAddr);
// 释放内存
Status = gBS->FreePages (MemoryAddr, Pages);
if (EFI_ERROR (Status)) {
Print (L"FreePages failed: %r\n", Status);
return Status;
}
Print (L"Memory freed successfully.\n");
return EFI_SUCCESS;
}
编译与运行
- 将上述代码保存为
MemAlloc.c,放入EDK2应用工程目录(如OvmfPkg/MemAlloc/)。 - 在工程的
.inf文件中声明源文件与依赖库:UefiLib、UefiBootServicesTableLib、PrintLib。 - 使用
build命令编译生成MemAlloc.efi。 - 在QEMU或真实UEFI Shell中运行:
fs0:\MemAlloc.efi。
验证方法
- 成功标志:屏幕依次输出分配页数、内存类型字符串(应为
EfiLoaderData)、非零起始地址,最后输出Memory freed successfully. - 错误排查:若输出
AllocatePages failed,检查是否在ExitBootServices之前调用(本代码运行于UEFI Shell,满足条件);若地址为0,说明分配失败。 - 类型验证:打印的地址应落在RAM范围内(QEMU默认1MB以下为保留区,通常分配结果在1MB以上),且类型与代码中指定的
EfiLoaderData一致。
预期输出示例
Allocated 1 page(s)
Memory Type: EfiLoaderData
Start Address: 0x3f00000
Memory freed successfully.
注:实际地址值由固件内存映射决定,每次运行可能不同,但类型与成功释放信息固定。