linux - 将 EFI 内存映射转换为 E820 映射

标签 linux x86 bootloader bios efi

我是 Linux 的新手,正在学习 Linux 如何了解可用的物理内存。我开始知道有一些 BIOS 系统调用 int 0x15,它将为您提供 E20 内存映射。

现在我找到一段代码,上面写着将 EFI 内存映射转换为 E820 内存映射的定义。上面是什么意思??

这是否意味着底层主板固件是基于 EFI 的,但由于此代码在 x86 上运行,我们需要将其转换为 E820 内存映射

如果是这样,x86 是否只知道 E820 内存映射?

E820 和 EFI 内存映射有什么区别??

期待得到详细的答案。

最佳答案

在这两种情况下,您拥有的是固件(BIOS 或 EFI),它负责检测实际物理插入的内存(以及多少),以及需要在 中了解此信息的操作系统某种格式

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

您在这里感到困惑的是 EFI 和 x86 是不兼容的——它们不是。 EFI 固件有自己的可用内存报告机制 - 具体来说,您可以使用 GetMemoryMap 引导服务(在调用 ExitBootServices 之前)从固件中检索内存映射。然而,至关重要的是,此内存映射采用 EFI 固件希望报告的格式 (EFI_MEMORY_DESCRIPTOR) 而不是 E820。在这种情况下,您不会同时尝试 int 15h,因为您已经拥有所需的信息。

我怀疑 Linux 内核所做的是使用 E820 格式作为 x86 架构上内存的内部表示。然而,在启动EFI时,内核必须使用EFI固件启动服务,而是选择将它得到的答案转换回E820格式。

这对于您正在编写的内核来说不是必需的。您只需要知道内存是如何映射的。

有些引导加载程序也会为您提供此信息,例如 GRUB。多重引导规范的一部分允许您指示引导加载程序它必须向您的内核提供此信息。

有关这方面的更多信息,永远有用的 osdev wiki有代码示例等。从 grub 获取内存映射的相关部分是 here .

进一步的观点:

出于多种原因,操作系统需要了解哪些内存映射到何处。一种是避免在固件服务所在的地方使用物理内存,另一种是为了与与 CPU 共享内存的设备进行通信。视频缓冲区就是一个常见的例子。

其次,在 EFI 中列出内存映射并不太困难。如果您还没有发现它,一些固件附带的 UEFI shell 有一个 memmap 命令来显示内存映射。如果你想自己实现这个,一个快速而肮脏的方法看起来像这样:

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;
}

这是一个相当简单的函数。如果您没有传入足够大的缓冲区,GetMemoryMap 会痛苦地提示,所以我们不断增加缓冲区大小,直到我们有足够的空间。然后我们循环并打印。请注意,sizeof(EFI_MEMORY_DESCRIPTOR) 实际上不是输出缓冲区中结构之间的区别 - 使用上面显示的返回大小计算,否则您最终会得到一个比实际大得多的表(而且地址空间看起来都是错误的)。

从该表中决定 E820 的通用格式并不难。

关于linux - 将 EFI 内存映射转换为 E820 映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17591351/

相关文章:

java - 如何使用 eclipse 的注释和 java 来编译我的程序?

c - 这个shellcode又让人头疼

python - Linux 服务器上的 HTTP 错误 429,但不是我的本地计算机?

linux - 在 Qt 中保存/打开对话框本地化

Java Mail - 如何在 Linux 上为共享文件夹创建链接并在 Windows 中访问该链接

assembly - 处理来自辅助 PIC : EOI order important? 的 x86 IRQ

c++ - 广播 __m128 vector 的任意一个元素

linux - 将 linux 根文件夹的子文件夹(/tmp、/lib、/etc)移动到另一个位置会影响系统吗?

assembly - 向BX寄存器写入值对ES寄存器有影响吗?

assembly - 引导加载程序加载自身而不是内核