为何按UEFI GetMemoryMap返回大小分配缓冲区后仍报EFI_BUFFER_TOO_SMALL?
UEFI GetMemoryMap() 分配精确大小仍返回BUFFER_TOO_SMALL是否正常?
问题场景
我用Zig编写UEFI应用时,调用两次GetMemoryMap():第一次获取所需缓冲区大小,随后用AllocatePool()分配对应大小的缓冲区,但第二次调用仍返回EFI_BUFFER_TOO_SMALL。添加mmap_size += descriptor_size * 2;后程序正常运行。
代码实现
fn getMemoryMap() uefi.Status { const log = std.log.scoped(.memory_map); const system_table = uefi.system_table; const boot_services = system_table.boot_services.?; var status: uefi.Status = undefined; var mmap_size: usize = 0; var mmap: ?[*]uefi.tables.MemoryDescriptor = null; var map_key: usize = undefined; var descriptor_size: usize = undefined; var descriptor_version: u32 = undefined; status = boot_services.getMemoryMap(&mmap_size, mmap, &map_key, &descriptor_size, &descriptor_version); switch (status) { .buffer_too_small => log.debug("Buffer too small, need {d} bytes for buffer", .{mmap_size}), else => { log.err("Excepted buffer_too_small but got {s} instead", .{@tagName(status)}); return status; }, } //mmap_size += descriptor_size * 2; status = boot_services.allocatePool(.loader_data, mmap_size, @ptrCast(&mmap)); switch (status) { .success => log.debug("Allocated {d} bytes for memory map at {*}", .{ mmap_size, mmap }), else => { log.err("Excepted success but got {s} instead", .{@tagName(status)}); return status; }, } status = boot_services.getMemoryMap(&mmap_size, mmap, &map_key, &descriptor_size, &descriptor_version); switch (status) { .success => log.debug("Got memory map", .{}), else => { log.err("Excepted success but got {s} instead", .{@tagName(status)}); return status; }, } return uefi.Status.success; }
运行日志
[debug(memory_map)]Buffer too small, need 5856 bytes for buffer [debug(memory_map)]Allocated 5856 bytes for memory map at [*]os.uefi.tables.MemoryDescriptor@2808018 [error(memory_map)]Excepted success but got buffer_too_small instead
回答
这种情况是完全正常的,属于UEFI规范定义的预期行为,核心原因如下:
1. 内存映射会在两次调用间动态变化
第一次调用GetMemoryMap()返回的mmap_size,仅代表调用瞬间系统内存映射所需的缓冲区大小。但在你调用AllocatePool()分配内存的过程中,系统内存映射已经发生了改变——AllocatePool()本身会向内存映射中添加一个新的MemoryDescriptor条目,用来描述刚分配的内存区域,这直接导致第二次调用GetMemoryMap()时,需要的缓冲区空间比第一次返回的数值更大。
2. UEFI规范的明确要求
UEFI规范中明确指出:调用者不能仅依赖第一次返回的精确大小分配缓冲区,必须预留额外的冗余空间(至少1~2个MemoryDescriptor的大小),以应对两次调用之间内存映射的动态变更。你添加的mmap_size += descriptor_size * 2正是为缓冲区预留了足够的冗余空间,因此第二次调用能够成功。
可行的处理方案
- 预分配冗余空间:在第一次获取的
mmap_size基础上,额外加上1~2倍的descriptor_size,这是最常用且高效的做法,能覆盖绝大多数场景下的内存映射变化。 - 循环重试机制:如果第二次调用仍返回
BUFFER_TOO_SMALL,则释放当前缓冲区,根据新返回的mmap_size重新分配更大的空间,再次调用GetMemoryMap(),直到调用成功。
内容的提问来源于stack exchange,提问作者OrderChaos
相关产品推荐
相关产品推荐

