首先,(可能在 (U)EFI 中)U 无法调用引导服务,这是获得所需内容的唯一方法,您可以在 ExitBootServices() 之前获取内存映射和帧缓冲区并将它们传递给内核,您'很幸运,因为我并不总是在这个平台上,我有一个支持 LEGACY BIOS 和 UEFI 的混合启动机制,这里是一个如何做到这一点的例子(我正在研究 EDK2,这是电喷)
这是您从 G.O.P 获取帧缓冲区的方式(已删除 UGA 文档):
FRAME_BUFFER_DESCRIPTOR* GraphicsOutputProtocolInitialize(){
// First, we need to query the firmware of all G.O.P Protocol Instances
// (Each instance may represent a GPU or a monitor, GOP features multiple-screens
// U have asked for a simple implementation so we will use only 1 frame buffer
EFI_STATUS status = 0;
EFI_HANDLE* HandleBuffer = NULL;
UINTN NumProtocolHandles = 0;
if(EFI_ERROR(gBS->LocateHandleBuffer(
ByProtocol, &gEfiGraphicsOutputProtocolGuid, NULL,
&NumProtocolHandles, &HandleBuffer
)) || !NumProtocolHandles) return NULL;
// Then u need to create a structure that you can pass to the kernel containing information about frame buffers
FRAME_BUFFER_DESCRIPTOR* FrameBuffer = NULL;
if(EFI_ERROR(gBS->AllocatePool(
EfiLoaderData, sizeof(FRAME_BUFFER_DESCRIPTOR), (void**)&FrameBuffer
))) ALLOCATION_PROBLEM;
ZeroMemory((void*)FrameBuffer, sizeof(FRAME_BUFFER_DESCRIPTOR));
EFI_GRAPHICS_OUTPUT_PROTOCOL* gop = NULL;
status = gBS->OpenProtocol(
HandleBuffer[0], // Get first Graphics Output Protocol Instance
&gEfiGraphicsOutputProtocolGuid,
(void**)&gop,
NULL,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if(EFI_ERROR(status) || !gop)
{
return NULL;
}
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* ginfo = NULL;
UINTN info_size = 0;
// if mode is not yet set by firmware, then set first mode
if(status == EFI_NOT_STARTED || !gop->Mode){
status = gop->SetMode(gop, 0);
if(EFI_ERROR(status) || !gop->Mode->Mode)
{
return NULL;
}
}
// Now we will ask firmware for the current Video Mode
status = gop->QueryMode(gop, gop->Mode->Mode, &info_size, &ginfo);
if(status != EFI_SUCCESS || !ginfo){
return NULL;
}
// You can also list availaible video modes
for(UINTN i = 0;i<gop->Mode->MaxMode;i++) {
status = gop->QueryMode(gop, i, &info_size, &ginfo);
if(status != EFI_SUCCESS || !ginfo){
return NULL;
}
// To set the mode :
status = gop->SetMode(gop, i);
if(EFI_ERROR(status))
{
return NULL;
}
}
FrameBuffer->FrameBufferSize = gop->Mode->FrameBufferSize;
FrameBuffer->HorizontalResolution = gop->Mode->Info->HorizontalResolution;
FrameBuffer->VerticalResolution = gop->Mode->Info->VerticalResolution;
FrameBuffer->FrameBufferBase = (char*)gop->Mode->FrameBufferBase;
ZeroMemory((void*)FrameBuffer, sizeof(FRAME_BUFFER_DESCRIPTOR));
}
return FrameBuffer;
}
这是获取内存映射的方式:
- 在第一次调用时,您将获得内存映射的大小(状态必须为 EFI_BUFFER_TOO_SMALL)
- 然后添加 2 * 描述符大小,因为总是有 2 个附加条目,然后为内存映射分配缓冲区
- 在第二次调用中,您将获得实际的内存映射(状态必须为 EFI_SUCCESS)
然后你就可以正常列出内存映射条目了
这就是你调用 GetMemoryMap() 的方式:
EFI_MEMORY_DESCRIPTOR* memory_map = NULL;
UINTN map_size = 0, map_key = 0, descriptor_size = 0;
// Must return EFI_BUFFER_TOO_SMALL on First Call
// map_size will contain buffer size needed
EFI_STATUS s = SystemTable->BootServices->GetMemoryMap(&map_size,memory_map,&map_key,&descriptor_size,&descriptor_version);
map_size+=2*descriptor_size; // this padding must be added since there is 2 additionnal entries
但是,您可以随时索取代码示例,(G.O.P) 之前的代码只是复制粘贴和修改以使其更易于理解,它未编译,因此可能包含一些错误。