MyToyOS: UEFI, a assombração reaparece!

Quanto mais eu penso que me livrei dos padrões da Microsoft, mais eles me perseguem…

Recentemente, no grupo “C & Assembly para x86-64”, no Facebook, eu disse que migraria o esboço do bootloader do MyToyOS para o padrão UEFI. O motivo é simples: O particionamento tradicional não permite discos muito grandes e UEFI tem a vantagem de não precisamos lidar com vários estágios de carga do bootloader.

Como UEFI funciona?

Nada mais simples: Trata-se de um simples arquivo executável (.EXE, mas com a extensão .EFI) no formato Portable Executable da Microsoft! Eis a assombração! O formato é esse:

Teremos que lidar com essa "engronha"...
Teremos que lidar com essa “engronha”…

O significado de cada campo pode ser obtido na especificação do padrão UEFI (aqui) e na especificação do formato PE (aqui).

Mas é interessante notar: O formato usado no UEFI é simplificado com relação a esse ai. Algumas sessões não existem nos arquivo .efi e a diferenciação sobre o que o executável faz é determinada pelo subsistema… Existem, pelo menos, 3 tipos de executáveis: Aplicações, bootloaders e firmware drivers.

Em essência, tudo o que temos que fazer é um arquivo em assembly contendo o header do formato cima, preenchido, e todas as demais rotinas podem ser feitas com o GCC (4.9 ou superior, porque eles permitem geração de código em 16 bits!).

Sim… o código é em 16 bits, como um executável para MS-DOS…

O que a ROM BIOS de sistemas que suportam UEFI faz é obter o arquivo da partição EFI e executá-lo… O arquivo pode ter, em teoria, até 2 GiB de tamanho. Na prática, é prudente mantê-lo com um tamanho inferior a 128 KiB (dois segmentos – não sei onde esse arquivo é carregado!), mas isso não é um limite rígido… A vantagem, é claro, é que não estamos mais restritos a um único setor de boot carregado pela BIOS! De fato, o código do MBR (Master Boot Record) desaparece completamente. Em uma de minhas máquinas de teste, onde tenho somente o UBUNTU 16.04 instalado. Obtendo o setor da MBR (LBA 0):

# dd if=/dev/sda of=disk.img bs=512 count=1

Obtenho isso:

part
Cadê a MBR? Não existe! Ou seja, temos apenas uma única entrada da tabela de partição tradicional (a partir do offset 0x1be, usada pela especificação da GUID Partition Table) e, mesmo assim, trata-se de uma entrada desabilitada, pois começa com 0x00, ao invés de 0x80. Provavelmente essa entrada aponta para a partição EFI.

A partição EFI, por sua vez, é uma partição FAT32:

# fdisk -l /dev/sda
Disk /dev/sda: 465,8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: 55DC090F-5E38-4521-A4E6-7B669BBB9ED6

Device         Start       End   Sectors   Size Type
/dev/sda1       2048   1050623   1048576   512M EFI System
/dev/sda2    1050624 960253951 959203328 457,4G Linux filesystem
/dev/sda3  960253952 976771071  16517120   7,9G Linux swap

# fdisk -l /dev/sda1
Disk /dev/sda1: 512 MiB, 536870912 bytes, 1048576 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: dos
Disk identifier: 0x00000000

O que tem dentro dessa partição? Os arquivos executáveis e, eventualmente, configurações:

# df /dev/sda1
Filesystem     1K-blocks  Used Available Use% Mounted on
/dev/sda1         523248  3684    519564   1% /boot/efi

# tree /boot/efi/
/boot/efi/
└── EFI
    └── ubuntu
        ├── fw/
        ├── fwupx64.efi
        ├── grub.cfg
        ├── grubx64.efi
        ├── MokManager.efi
        └── shimx64.efi

O grub colocou esses arquivos todos ai… Cada um deles, se estiver no formato correto, é executado pela BIOS quanto o sistema inicia. Simples assim. No caso, grubx64.efi lê o arquivo grub.cfg para “bootar” o sistema na partição correta. O executável fwupx64.efi, provavelmente, é usado pelo grub para o caso de termos firmwares fora do padrão, onde drivers são colocados no diretório fw/. Não me pergunte o que seria o MokManager e o shimx64… pergunte à documentação do grub 2.

Por que a especificação é tão grande?

O documento da especificação UEFI mostra um conjunto de facilidades disponíveis quando você usa o UEFI SDK (Software Development Kit). Por exemplo, o executável pode pedir para a BIOS iniciar o modo protegido pra você, tanto em 32 bits quanto em 64… Mas, prepare-se para adotar um esquema bem diferente de convenções de chamadas de funções e obter um executável gigantesco no processo…

Não recomendo o uso do SDK por esse motivo e por outro mais prático: Uma das reclamações da adoção do padrão EFI é justamente o lobby que empresas como a Microsoft têm feito para criar bootloaders “seguros” que exijam o uso de criptografia. Na prática, eles querem controlar quem tenta instalar seus sistemas (somente comprando uma chave vocẽ poderia “bootar” o Windows, por exemplo)… Isso só é possível ao usar o UEFI SDK.

E quanto ao MyToyOS?

Bem… sem a limitação do tamanho dos estágios 0 e 1 podemos criar um bootloader mais complexo, até mesmo em modo gráfico (320×200 de 256 cores, VGA, por exemplo, ou usando um dos modos VESA). Além do header MZ e PE, o código pode ser quase totalmente feito em C e terá que achar onde se encontra a partição na GPT, pular para o modo protegido, carregar o kernel na memória alta e pular para ele… Vale a pena dar uma olhada no código de boot do Linux (no diretório arch/x86/boot/ dos fontes mais recentes). O arquivo header.S é o início de tudo…

Em teoria, tudo o que temos que fazer é colocar o executável num diretório EFI/mytoyos/ na partição EFI… Bem como criar a partição com o kernel… Dá até vontade de fazer uma brincadeira como o logo do AmigaDOS:

amigados

Hehehe…

Softwares de virtualização aceitam EFI?

Aceitam… No VirtualBox basta habilitar a opção:

vbox

No caso do QEMU, provavelmente você terá que instalar o pacote ovmf.

Ao que parece, no VMWare, existe um tweaker que habilita EFI também (não tenho certeza, não uso VMWare!).

Anúncios