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;
}

编译与运行

  1. 将上述代码保存为 MemAlloc.c,放入EDK2应用工程目录(如 OvmfPkg/MemAlloc/)。
  2. 在工程的 .inf 文件中声明源文件与依赖库:UefiLib、UefiBootServicesTableLib、PrintLib。
  3. 使用 build 命令编译生成 MemAlloc.efi。
  4. 在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.

注:实际地址值由固件内存映射决定,每次运行可能不同,但类型与成功释放信息固定。