调用ExitBootServices()后帧缓冲区失效,图形功能无法正常工作求助
你遇到的问题核心是:退出UEFI Boot Services后,原GOP帧缓冲区的内存地址不再可用。
UEFI Boot Services运行期间,系统会统一管理所有内存资源;当调用ExitBootServices()后,UEFI会回收所有未被显式标记为“保留”的内存,包括帧缓冲区所在的内存区域——如果没提前保留,这块内存会被释放或重新映射,导致你之前保存的地址失效。而退出前能正常工作,是因为此时Boot Services还在运行,内存管理机制尚未生效。
解决步骤
要在退出Boot Services后继续使用帧缓冲区,必须提前将其内存区域标记为保留:
获取当前内存映射
调用BS->GetMemoryMap获取系统内存的描述符数组,需要提前分配足够的缓冲区来存储这些描述符。定位帧缓冲对应的内存条目
遍历内存描述符,找到与gop->Mode->FrameBufferBase匹配的条目(同时要检查FrameBufferSize是否覆盖该区域)。标记该内存为保留
将找到的内存描述符的Type字段修改为EfiReservedMemoryType,确保UEFI在退出Boot Services时不会回收这块内存。传递更新后的内存映射调用ExitBootServices
调用BS->ExitBootServices时,必须传入更新后的内存映射(包括你修改的保留条目)。注意:posix-uefi的exit_bs()封装了这个过程,你需要确保它使用的是修改后的内存映射,或者自己实现退出逻辑来处理这一步。
代码调整示例(关键部分)
// 在获取GOP信息后,添加内存保留逻辑 efi_uintn_t map_size, desc_size, map_key; efi_memory_descriptor_t *mem_map = NULL; efi_status_t status; // 先获取内存映射的大小 status = BS->GetMemoryMap(&map_size, NULL, &map_key, &desc_size, NULL); if (EFI_ERROR(status)) { return false; } // 分配内存存储内存映射 mem_map = BS->AllocatePool(EfiLoaderData, map_size); if (!mem_map) { return false; } // 获取完整内存映射 status = BS->GetMemoryMap(&map_size, mem_map, &map_key, &desc_size, NULL); if (EFI_ERROR(status)) { BS->FreePool(mem_map); return false; } // 遍历内存描述符,找到帧缓冲对应的区域 efi_uintn_t frame_buffer_size = gop->Mode->FrameBufferSize; efi_physical_addr_t frame_buffer_base = gop->Mode->FrameBufferBase; for (efi_uintn_t i = 0; i < map_size / desc_size; i++) { efi_memory_descriptor_t *desc = (efi_memory_descriptor_t*)((char*)mem_map + i * desc_size); if (frame_buffer_base >= desc->PhysicalStart && frame_buffer_base + frame_buffer_size <= desc->PhysicalStart + desc->NumberOfPages * EFI_PAGE_SIZE) { // 标记为保留内存 desc->Type = EfiReservedMemoryType; break; } } // 调用ExitBootServices时传入更新后的内存映射 status = BS->ExitBootServices(ImageHandle, map_key); if (EFI_ERROR(status)) { // 如果失败,可能需要重新获取内存映射(因为映射可能已变化) BS->FreePool(mem_map); fail("failed to exit boot services"); return false; } BS->FreePool(mem_map); // 现在可以正常使用帧缓冲区了 for (int i = 0; i < init_info.video_height; i++) { *((uint32_t *)(init_info.frame_buffer + init_info.video_pitch * i + 4 * i)) = 0xFFFF00; }
注意:如果posix-uefi的exit_bs()函数已经封装了内存映射的处理,你需要替换它或者修改其内部逻辑,确保帧缓冲内存被保留。
内容的提问来源于stack exchange,提问作者cam0347

