Resolvendo minha dúvida sobre a carga do bootloader (UEFI)

Uma das dúvidas que tive ao começar a estudar UEFI foi “Em que endereço físico o loader é carregado?”. No modo legado, via serviço da BIOS (INT 0x19), o Master Boot Record é carregado sempre no endereço físico 0x7C00 (endereço lógico 0:0x7c00). Isso dá uma boa base para trabalhar a carga de qualquer loader mais complexo que apenas 1 setor (512 bytes), mas não temos (em teoria) a mesma certeza com relação ao loader em modo protegido que o UEFI carrega na memória.

Felizmente, UEFI carrega o executável numa região bem alta da memória e deixa muito espaço disponível para carregarmos o “kernel”. Isso pode ser visto através do comando memmap.

A listagem abaixo foi obtida numa VM, usando QEMU (qemu-system-x86_64) e a BIOS OVMF, configurada com apenas 1 GiB de RAM (retirei os “atributos” para ficar melhor formatado aqui no blog):

FS0:> memmap
Type       Start            End              # Pages
BS_Code    0000000000000000-0000000000000FFF 0000000000000001
Available  0000000000001000-000000000009FFFF 000000000000009F
Available  0000000000100000-00000000007FFFFF 0000000000000700
ACPI_NVS   0000000000800000-0000000000807FFF 0000000000000008
Available  0000000000808000-000000000080FFFF 0000000000000008
ACPI_NVS   0000000000810000-00000000008FFFFF 00000000000000F0
BS_Data    0000000000900000-00000000011FFFFF 0000000000000900
Available  0000000001200000-000000003BFBDFFF 000000000003ADBE
BS_Data    000000003BFBE000-000000003BFDDFFF 0000000000000020
Available  000000003BFDE000-000000003E55DFFF 0000000000002580
LoaderCode 000000003E55E000-000000003E686FFF 0000000000000129
Available  000000003E687000-000000003E72BFFF 00000000000000A5
BS_Data    000000003E72C000-000000003E748FFF 000000000000001D
Available  000000003E749000-000000003E765FFF 000000000000001D
BS_Data    000000003E766000-000000003E79BFFF 0000000000000036
Available  000000003E79C000-000000003E79DFFF 0000000000000002
BS_Data    000000003E79E000-000000003E7A6FFF 0000000000000009
Available  000000003E7A7000-000000003E7A7FFF 0000000000000001
BS_Data    000000003E7A8000-000000003E9F7FFF 0000000000000250
ACPI_NVS   000000003E9F8000-000000003EA0BFFF 0000000000000014
Reserved   000000003EA0C000-000000003EA29FFF 000000000000001E
BS_Code    000000003EA2A000-000000003ECBEFFF 0000000000000295
RT_Data    000000003ECBF000-000000003EDA4FFF 00000000000000E6
RT_Code    000000003EDA5000-000000003EE40FFF 000000000000009C
BS_Data    000000003EE41000-000000003FD40FFF 0000000000000F00
BS_Code    000000003FD41000-000000003FEC0FFF 0000000000000180
RT_Code    000000003FEC1000-000000003FEF0FFF 0000000000000030
RT_Data    000000003FEF1000-000000003FF14FFF 0000000000000024
Reserved   000000003FF15000-000000003FF18FFF 0000000000000004
ACPI_Recl  000000003FF19000-000000003FF20FFF 0000000000000008
ACPI_NVS   000000003FF21000-000000003FF24FFF 0000000000000004
BS_Data    000000003FF25000-000000003FFCFFFF 00000000000000AB
RT_Data    000000003FFD0000-000000003FFEFFFF 0000000000000020
Available  000000003FFF0000-000000003FFFFFFF 0000000000000010
 
  Reserved  :             34 Pages (139,264 Bytes)
  LoaderCode:            297 Pages (1,216,512 Bytes)
  LoaderData:              0 Pages (0 Bytes)
  BS_Code   :          1,046 Pages (4,284,416 Bytes)
  BS_Data   :          7,031 Pages (28,798,976 Bytes)
  RT_Code   :            204 Pages (835,584 Bytes)
  RT_Data   :            298 Pages (1,220,608 Bytes)
  ACPI_Recl :              8 Pages (32,768 Bytes)
  ACPI_NVS  :            272 Pages (1,114,112 Bytes)
  MMIO      :              0 Pages (0 Bytes)
  MMIO_Port :              0 Pages (0 Bytes)
  PalCode   :              0 Pages (0 Bytes)
  Available :        252,858 Pages (1,035,706,368 Bytes)
              -------------- 
Total Memory:          1,023 MB (1,073,209,344 Bytes)

Aqui o “BS” de BS_Code e BS_Data significa “Boot Services“, e “RT”, “RunTime services“… Existem áreas reservadas e reclamadas pelo ACPI e, lá no meio, temos “LoaderCode“:

LoaderCode 000000003E55E000-000000003E686FFF 0000000000000129

Com 297 páginas reservadas (ou, cerca de 1.16 MiB)… Note que temos 165 páginas (“disponíveis” logo à seguir, o que nos dá quase 700 KiB), provavlemente para o segmento de dados do loader.

Para um “loader”, 2 MiB é mais que suficiente! Considere agora que o endereço físico “tradicional” para a carga de kernels (0x100000) têm 1792 páginas disponíveis, ou 7 MiB! E, mesmo que seu kernel não caiba nesse espaço, temos, no total, quase 988 MiB disponíveis em 10 pedaços, onde as partes marcadas em verde são as realmente usáveis e a em vermelho será usada pelo loader:

Type       Start            End              # Pages
Available  0000000000001000-000000000009FFFF 000000000000009F
Available  0000000000100000-00000000007FFFFF 0000000000000700
Available  0000000000808000-000000000080FFFF 0000000000000008
Available  0000000001200000-000000003BFBDFFF 000000000003ADBE
Available  000000003BFDE000-000000003E55DFFF 0000000000002580
Available  000000003E687000-000000003E72BFFF 00000000000000A5
Available  000000003E749000-000000003E765FFF 000000000000001D
Available  000000003E79C000-000000003E79DFFF 0000000000000002
Available  000000003E7A7000-000000003E7A7FFF 0000000000000001
Available  000000003FFF0000-000000003FFFFFFF 0000000000000010

Vale lembrar que, depois que o loader entrega o controle ao kernel, podemos descarregar todo o Boot Services e o RunTime services, liberando mais 34 MiB de RAM… E se tivermos mais que apenas 1 GiB de RAM, mais espaços serão disponibilizados…

É bom lembrar, também, que os espaços de endereços relativos aos dispositivos PCI, PCIe, LAPIC, I/O PIC e outros só não constam dessa lista porque não requisitei mais que 1 GiB, mas eles estão lá e disponíveis, no UEFI, via “protocolos”.

Anúncios