Преобразование карты памяти EFI в карту E820

Я новичок в Linux и рассказываю о том, как Linux узнает о доступной физической памяти. Я узнал, что есть системный вызов BIOS int 0x15, который даст вам карту памяти E20.

Теперь я нахожу фрагмент кода, в котором говорится, что он предназначен для преобразования карты памяти EFI в карту памяти E820. Что выше означает??

Означает ли это, что базовая прошивка материнской платы основана на EFI, но поскольку этот код работает на x86, нам нужно преобразовать его в карту памяти E820

Если так, x86 знает только о картах памяти E820??

В чем разница между картами памяти E820 и EFI?

Ждем подробного ответа на этот же.

1 ответ

Решение

В обоих случаях у вас есть прошивка (BIOS или EFI), которая отвечает за определение того, какая память (и сколько) фактически физически подключена, и операционная система, которая должна знать эту информацию в каком-либо формате.

Означает ли это, что базовая прошивка материнской платы основана на EFI, но поскольку этот код работает на x86, нам нужно преобразовать его в карту памяти E820

Ваше заблуждение заключается в том, что EFI и x86 несовместимы - это не так. Встроенное ПО EFI имеет свои собственные механизмы для сообщения о доступной памяти - в частности, вы можете использовать GetMemoryMap служба загрузки (перед тем, как вызывать ExitBootServices) получить карту памяти из прошивки. Однако, что очень важно, эта карта памяти имеет формат, который желает сообщить прошивка EFI (EFI_MEMORY_DESCRIPTOR) а не е820. В этом случае вы также не будете пытаться int 15h, поскольку у вас уже есть информация, которая вам нужна.

Я подозреваю, что ядро ​​Linux использует формат E820 в качестве внутреннего представления памяти в архитектуре x86. Однако при загрузке EFI ядро ​​должно использовать службы загрузки прошивки EFI, но выбирает конвертированный ответ, который возвращается обратно в формат E820.

Это не обязательно для ядра, которое вы пишете. Вам просто нужно знать, как отображается память.

Это также тот случай, когда некоторые загрузчики предоставят вам эту информацию, например, GRUB. Часть спецификации мультизагрузки позволяет вам указать загрузчику, что он должен предоставить эту информацию вашему ядру.

Более подробно об этом всегда есть в вики osdev, где есть примеры кода и т. Д. Соответствующие разделы для получения карт памяти из grub находятся здесь.

Дальнейшие пункты:

Операционная система должна понимать, какая память отображена в каком месте по нескольким причинам. Один из них заключается в том, чтобы избегать использования физической памяти, в которой находятся службы встроенного программного обеспечения, а другой - для связи с устройствами, которые совместно используют память с процессором. Видеобуфер является типичным примером этого.

Во-вторых, перечислить карту памяти в EFI не так уж сложно. Если вы еще этого не обнаружили, оболочка UEFI, поставляемая с некоторыми прошивками, имеет 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.

Другие вопросы по тегам