You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UEFI应用内存大小计算异常,QEMU测试显示错误值求助

问题分析与修复

你的代码存在三个核心问题,导致内存大小计算错误、NumberOfPages出现随机负数:

1. 内存描述符指针偏移计算错误

GMS函数中,你用(UINT64*)MMap + i * MMAPDESCSIZE计算第i个描述符的地址,这是完全错误的:

  • (UINT64*)MMap会将指针按8字节(UINT64长度)步长偏移,而i * MMAPDESCSIZE是字节数,两者叠加会让指针偏移量放大8倍,直接越界访问内存,读取到随机值(包括被当作有符号数解析的负数)。
  • 正确做法是按字节偏移,先把MMap转成UINT8*类型,再加上字节偏移量。

2. Print函数格式符不匹配

UEFI的Print函数要求格式符与变量类型严格匹配:

  • UINT64类型需要用%Lu格式符,你用%d(对应32位有符号整数)会导致64位数值被截断,高位被当作符号位解析,出现负数。
  • 所有UINT64类型的变量(MapEntries、MapSize、MemSizeB等)都要对应正确的格式符。

3. GetMemoryMap调用缺失错误处理

两次调用GetMemoryMap和AllocatePool都没有检查返回值,若内存分配失败或内存map获取失败,会导致后续操作访问无效内存,引发未知错误。


修正后的完整代码

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>

UINT64
GMS(EFI_MEMORY_DESCRIPTOR* MMap, UINT64 MMAPE, UINT64 MMAPDESCSIZE)
{
    UINT64 MemSizeB = 0; // 移除static,避免多次调用返回旧值

    for (UINT64 i = 0; i < MMAPE; i++){
        // 按字节偏移计算第i个描述符的地址
        EFI_MEMORY_DESCRIPTOR *desc = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)MMap + i * MMAPDESCSIZE);
        // 可选:只统计可用内存类型,按需调整
        if (desc->Type == EfiConventionalMemory || desc->Type == EfiLoaderCode || desc->Type == EfiLoaderData) {
            MemSizeB += desc->NumberOfPages * EFI_PAGE_SIZE;
        }
    }

    return MemSizeB;
}

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
    EFI_MEMORY_DESCRIPTOR* Map = NULL;
    UINTN MapSize, MapKey, DescSize;
    UINT32 DescVer;
    EFI_STATUS Status;

    // 第一次调用获取所需内存大小
    Status = SystemTable->BootServices->GetMemoryMap(&MapSize, NULL, &MapKey, &DescSize, &DescVer);
    if (EFI_ERROR(Status)) {
        Print(L"Failed to get memory map size: %r\n", Status);
        return Status;
    }

    // 额外多分配一个描述符的空间,避免内存map变化导致溢出
    Status = SystemTable->BootServices->AllocatePool(EfiLoaderData, MapSize + DescSize, (void**)&Map);
    if (EFI_ERROR(Status)) {
        Print(L"Failed to allocate pool: %r\n", Status);
        return Status;
    }

    // 第二次调用获取实际内存map
    Status = SystemTable->BootServices->GetMemoryMap(&MapSize, Map, &MapKey, &DescSize, &DescVer);
    if (EFI_ERROR(Status)) {
        Print(L"Failed to get memory map: %r\n", Status);
        SystemTable->BootServices->FreePool(Map);
        return Status;
    }

    UINT64 MapEntries = MapSize / DescSize;
    Print(L"Map Entries: %Lu\n", MapEntries);
    Print(L"Map Size: %Lu\n", (UINT64)MapSize);
    Print(L"Descriptor Size: %Lu\n", (UINT64)DescSize);

    UINT64 TotalMem = GMS(Map, MapEntries, DescSize);
    Print(L"Total usable memory: %Lu MB\n", TotalMem / 1024 / 1024);

    // 释放分配的内存
    SystemTable->BootServices->FreePool(Map);

    return EFI_SUCCESS;
}

额外说明

  • 移除了MemSizeB的static修饰:多次调用GMS时会重新计算内存大小,避免调试或重复执行时返回旧值。
  • 增加内存类型过滤:默认只统计常规内存、加载器代码/数据区域,若需统计所有内存类型,可移除if判断。
  • 内存分配额外预留空间:遵循UEFI规范,避免两次GetMemoryMap调用之间内存map变化导致空间不足。
  • 增加资源释放:使用完内存map后调用FreePool释放内存,符合UEFI内存管理要求。

内容的提问来源于stack exchange,提问作者Kitcha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:56:08