Question

I am new to Linux and learing about how Linux comes to know about the avaible Physical Mmeory .I came to know there are some BIOS system call int 0x15 which willl gives you E20 memory Map.

Now I find a piece of code where its says its defination for converting EFI memory map to E820 Memory map.What does above mean??

Is it meant underlaying motherboard firmware is EFI based but since this code runs on x86 we need to convert it to E820 Memory Map

If so ,does x86 knows only about E820 memory maps??

What is difference between E820 and EFI memory maps??

Looking forward to get detailed answer on same.

Was it helpful?

Solution

In both cases, what you have is your firmware (BIOS or EFI) which is responsible for detecting what memory (and how much) is actually physically plugged in, and the operating system which needs to know this information in some format.

Is it meant underlaying motherboard firmware is EFI based but since this code runs on x86 we need to convert it to E820 Memory Map

Your confusion here is that EFI and x86 are incompatible - they aren't. EFI firmware has its own mechanisms for reporting available memory - specifically, you can use the GetMemoryMap boot service (before you invoke ExitBootServices) to retrieve the memory map from the firmware. However, critically, this memory map is in the format the EFI firmware wishes to report (EFI_MEMORY_DESCRIPTOR) rather than E820. In this scenario, you would not also attempt int 15h, since you already have the information you need.

I suspect what the Linux kernel does is to use the E820 format as its internal representation of memory on the x86 architecture. However, when booting EFI, the kernel must use the EFI firmware boot services, but chooses the convert the answer it gets back to the E820 format.

This is not a necessary thing for a kernel you are writing to do. You simply need to know how memory is mapped.

It is also the case that some bootloaders will provide this information for you, for example GRUB. Part of the multiboot specification allows you to instruct the bootloader that it must provide this information to your kernel.

For more on this, the ever-useful osdev wiki has code samples etc. The relevant sections for getting memory maps from grub are here.

Further points:

The OS needs to understand what memory is mapped where for several reasons. One is to avoid using physical memory where firmware services reside, but the other is for communication with devices who share memory with the CPU. The video buffer is a common example of this.

Secondly, listing the memory map in EFI is not too difficult. If you haven't already discovered it, the UEFI shell that comes with some firmware has a memmap command to display the memory map. If you want to implement this yourself, a quick and dirty way to do that looks like this:

EFI_STATUS EFIAPI PrintMemoryMap(EFI_SYSTEM_TABLE* SystemTable)
{
    EFI_STATUS status = EFI_SUCCESS;
    UINTN MemMapSize = sizeof(EFI_MEMORY_DESCRIPTOR)*16;
    UINTN MemMapSizeOut = MemMapSize;
    UINTN MemMapKey = 0; UINTN MemMapDescriptorSize = 0;
    UINT32 MemMapDescriptorVersion = 0;
    UINTN DescriptorCount = 0;
    UINTN i = 0;
    uint8_t* buffer = NULL;
    EFI_MEMORY_DESCRIPTOR* MemoryDescriptorPtr = NULL;

    do 
    {
        buffer = AllocatePool(MemMapSize);
        if ( buffer == NULL ) break;

        status = gBS->GetMemoryMap(&MemMapSizeOut, (EFI_MEMORY_DESCRIPTOR*)buffer, 
            &MemMapKey, &MemMapDescriptorSize, &MemMapDescriptorVersion);

        Print(L"MemoryMap: Status %x\n", status);
        if ( status != EFI_SUCCESS )
        {
            FreePool(buffer);
            MemMapSize += sizeof(EFI_MEMORY_DESCRIPTOR)*16;
        }
    } while ( status != EFI_SUCCESS );

    if ( buffer != NULL )
    {
        DescriptorCount = MemMapSizeOut / MemMapDescriptorSize;
        MemoryDescriptorPtr = (EFI_MEMORY_DESCRIPTOR*)buffer;

        Print(L"MemoryMap: DescriptorCount %d\n", DescriptorCount);

        for ( i = 0; i < DescriptorCount; i++ )
        {
            MemoryDescriptorPtr = (EFI_MEMORY_DESCRIPTOR*)(buffer + (i*MemMapDescriptorSize));
            Print(L"Type: %d PhsyicalStart: %lx VirtualStart: %lx NumberofPages: %d Attribute %lx\n",
                MemoryDescriptorPtr->Type, MemoryDescriptorPtr->PhysicalStart,
                MemoryDescriptorPtr->VirtualStart, MemoryDescriptorPtr->NumberOfPages,
                MemoryDescriptorPtr->Attribute);
        }
        FreePool(buffer);
    }

    return status;
}

This is a reasonably straightforward function. GetMemoryMap complains bitterly if you don't pass in a large enough buffer, so we keep incrementing the buffer size until we have enough space. Then we loop and print. Be aware that sizeof(EFI_MEMORY_DESCRIPTOR) is in fact not the difference between structs in the output buffer - use the returned size calculation shown above, or you'll end up with a much larger table than you really have (and the address spaces will all look wrong).

It wouldn't be massively difficult to decide on a common format with E820 from this table.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top