kzinti wrote:
You are not supposed to guess the size of descriptors like you are doing with STUPID_EXTRA_BEHIND_DESCRIPTOR. You are supposed to first call GetMemoryMap() with 0 size and null buffer to obtain the descriptor size and then use that to allocate memory.
There is also no need for this exponential buffer growth thing you are doing with a shift... GetMemoryMap() is returning the size it wants in the first parameter. There is no need to guess here.
And yes, you need to initialize your variables otherwise you will get EFI_INVALID_PARAMETER.
You can also get EFI_INVALID_PARAMETER when calling ExitBootServices(). This can happen if ExitBootServices() itself modifies the memory map, in which case the key your obtained isn't valid anymore.
Here is how I do it:
Code:
void EfiBoot::Exit(MemoryMap& memoryMap)
{
UINTN size = 0;
EFI_MEMORY_DESCRIPTOR* descriptors = nullptr;
UINTN memoryMapKey = 0;
UINTN descriptorSize = 0;
UINT32 descriptorVersion = 0;
// 1) Retrieve the memory map from the firmware
EFI_STATUS status;
std::vector<char> buffer;
while ((status = g_efiBootServices->GetMemoryMap(&size, descriptors, &memoryMapKey, &descriptorSize, &descriptorVersion)) == EFI_BUFFER_TOO_SMALL)
{
// Extra space to try to prevent "partial shutdown" when calling ExitBootServices().
// See comment below about what a "partial shutdown" is.
size += descriptorSize * 10;
buffer.resize(size);
descriptors = (EFI_MEMORY_DESCRIPTOR*)buffer.data();
}
if (EFI_ERROR(status))
{
Fatal("Unable to retrieve the EFI memory map: %p\n", status);
}
// 2) Exit boot services - it is possible for the firmware to modify the memory map
// during a call to ExitBootServices(). A so-called "partial shutdown".
// When that happens, ExitBootServices() will return EFI_INVALID_PARAMETER.
while ((status = g_efiBootServices->ExitBootServices(g_efiImage, memoryMapKey)) == EFI_INVALID_PARAMETER)
{
// Memory map changed during ExitBootServices(), the only APIs we are allowed to
// call at this point are GetMemoryMap() and ExitBootServices().
size = buffer.size(); // Probably not needed, but let's play safe since EFI could change that value behind our back (you never know!)
status = g_efiBootServices->GetMemoryMap(&size, descriptors, &memoryMapKey, &descriptorSize, &descriptorVersion);
if (EFI_ERROR(status))
{
break;
}
}
if (EFI_ERROR(status))
{
Fatal("Unable to exit boot services: %p\n", status);
}
memoryMap.Build(descriptors, size / descriptorSize, descriptorSize);
}
Cheers, I've applied this in my code (see below) and it passes the memory map through as it did before, however the values are still garbled.
Code:
Print(L"Getting Memory Map and Exiting Boot Services... ");
EFI_MEMORY_DESCRIPTOR* memMap = NULL;
UINTN memMapSize = 0, memMapKey = 0, descriptorSize = 0;
UINT32 descriptorVer = 0;
{
while ((status = SystemTable->BootServices->GetMemoryMap(&memMapSize, memMap, &memMapKey, &descriptorSize, &descriptorVer)) == EFI_BUFFER_TOO_SMALL) {
memMapSize += descriptorSize * 10;
if (memMap != NULL) SystemTable->BootServices->FreePool(memMap);
SystemTable->BootServices->AllocatePool(EfiLoaderData, memMapSize, (void**)&memMap);
}
if (EFI_ERROR(status)) {
Print(L"FATAL - Unable to retrieve EFI memory map [%d]\n\r", status);
while (1);
}
while ((status = SystemTable->BootServices->ExitBootServices(ImageHandle, memMapKey)) == EFI_INVALID_PARAMETER) {
status = SystemTable->BootServices->GetMemoryMap(&memMapSize, memMap, &memMapKey, &descriptorSize, &descriptorVer);
if (EFI_ERROR(status)) break;
}
if (EFI_ERROR(status)) {
Print(L"FATAL - Unable to exit boot services [%d]\n\r", status);
while (1);
}
}
It still yields this: