O Processo de Boot
A sequência entre ligar a energia de uma máquina e a primeira instrução de um kernel sendo executada abrange vários donos distintos da CPU, cada um passando o controle ao próximo através de uma convenção bem definida, ainda que de certa forma acumulada historicamente. Entender essa sequência importa para o desenvolvimento de kernel especificamente porque o código mais inicial de um kernel herda qualquer estado que o estágio anterior tenha deixado: um ambiente com apenas as garantias que o firmware e um bootloader escolheram fornecer, e consideravelmente menos do que a CPU é, em última instância, capaz de oferecer.
Firmware
Seção intitulada “Firmware”O primeiríssimo código a executar em qualquer máquina x86 pertence ao firmware, não a um sistema operacional: seja uma BIOS legada ou, em hardware atual, UEFI. O firmware realiza o Power-On Self Test (POST), inicializa hardware essencial incluindo o controlador de memória, e então localiza algo para o qual passar o controle. É também daqui que se origina o mapa de memória física do qual um kernel eventualmente depende, já que o firmware é o que de fato consultou o controlador de memória para produzi-lo.
Uma BIOS localiza um dispositivo inicializável lendo seu primeiro setor de 512 bytes, o Master Boot Record, e verificando os bytes de assinatura 0x55, 0xAA bem no final dele; se presentes, a BIOS carrega esse setor inteiro para o endereço físico 0x7C00 e salta para ele, ainda em real mode de 16 bits, com essencialmente mais nada configurado. A UEFI, em vez disso, localiza e executa diretamente um executável no formato PE .efi a partir de uma EFI System Partition formatada em FAT, rodando com muito mais da plataforma já inicializada, incluindo, notavelmente, já em um modo de endereçamento plano e não segmentado em vez do esquema segmento:offset do real mode, razão pela qual código de boot voltado a UEFI se parece consideravelmente mais com C comum do que com o assembly que um boot sector da era BIOS geralmente exige.
O bootloader
Seção intitulada “O bootloader”Como um setor MBR legado tem apenas 512 bytes para trabalhar (menos os dois bytes consumidos pela assinatura de boot), ele raramente contém diretamente a lógica real de carregamento de um kernel. Em vez disso, tipicamente carrega um bootloader de segundo estágio maior a partir do disco, que então realiza o trabalho mais envolvido: ler uma imagem de kernel do sistema de arquivos, analisar qualquer formato que aquele kernel use para descrever seus próprios requisitos de carregamento (comumente Multiboot), transicionar a CPU para fora do real mode, configurar um ambiente mínimo no qual o kernel possa confiar, e finalmente saltar para o próprio ponto de entrada do kernel. Muitos kernels hobby evitam escrever esse estágio inteiramente dependendo de um bootloader já existente, como o GRUB, que já implementa carregamento Multiboot, análise de sistema de arquivos, e a transição de real para protected mode, ao custo de depender de uma ferramenta externa em vez de controlar cada passo pessoalmente, uma troca discutida com mais profundidade em Bootloaders.
Transições de modo
Seção intitulada “Transições de modo”Seja lá o que carregue o kernel (um bootloader customizado ou um já existente como o GRUB) é responsável por tirar a CPU do real mode de 16 bits em que ela começa, já que essencialmente nenhum kernel pretende rodar ali permanentemente. A transição para protected mode de 32 bits exige uma Global Descriptor Table já carregada e o bit de habilitação de proteção de CR0 definido, seguido imediatamente por um far jump para esvaziar o pipeline interno de instruções da CPU de quaisquer instruções decodificadas em real mode ainda em trânsito. A linha A20 também precisa ser habilitada perto desse mesmo ponto, antes que algo dependa de memória acima de 1 MB se comportar corretamente. Um kernel visando long mode de 64 bits ainda tem trabalho adicional pela frente mesmo depois disso (o long mode exige adicionalmente que a paginação já esteja ativa com PAE habilitado), razão pela qual muitos kernels realizam essa segunda transição por conta própria, logo em seu próprio ponto de entrada, em vez de esperar que um bootloader a tenha completado em seu nome; o próprio carregamento Multiboot do GRUB, notavelmente, passa o controle em protected mode de 32 bits, não em long mode de 64 bits, deixando esse passo final para o kernel.
Alcançando o ponto de entrada do kernel
Seção intitulada “Alcançando o ponto de entrada do kernel”No momento em que o próprio código de um kernel começa a executar, exatamente quanto do estado da CPU é confiável depende inteiramente do que o carregou e por qual convenção. Um bootloader compatível com Multiboot garante um conjunto específico e documentado de fatos na entrada: um valor mágico particular em um registrador confirmando que Multiboot foi de fato usado, um ponteiro para a estrutura de informação de boot (contendo o mapa de memória e outros detalhes) em outro, paginação desabilitada, interrupções desabilitadas; e o código de entrada de um kernel pode confiar exatamente nesse conjunto de garantias e nada mais. Um kernel iniciado por meios menos padronizados tem correspondentemente menos garantias e precisa estabelecer mais de seu próprio ambiente (uma stack válida, uma GDT em que confia, uma IDT) antes de poder fazer com segurança praticamente qualquer coisa além da configuração mais mínima.
Notas de implementação
Seção intitulada “Notas de implementação”O limite de 512 bytes de um boot sector é mais rígido na prática do que parece à primeira vista, já que precisa incluir não só código mas a assinatura de boot no final, e quaisquer dados que o próprio código precise (strings, uma GDT parcial, parâmetros de leitura de disco) competem com instruções pelo mesmo orçamento minúsculo: um exercício inicial comum é simplesmente conseguir imprimir uma mensagem de “hello world” dentro dessa restrição antes de tentar qualquer coisa mais elaborada. A transição para fora do real mode também é incomumente implacável com pequenos erros: um far jump ausente depois de definir o bit de habilitação de proteção, ou uma entrada de GDT com base ou limite incorretos, tende a produzir um triple fault imediato e um reboot silencioso, em vez de qualquer mensagem de erro diagnosticável, precisamente porque a maquinaria que normalmente reportaria um erro é ela mesma parte do que ainda não foi corretamente estabelecido.
Referências
Seção intitulada “Referências”- ^ Intel, Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A, Capítulo 9: descreve o estado do processador no power-on e a transição para protected mode.
- ^ Especificação UEFI: descreve o fluxo de boot UEFI como alternativa ao boot legado BIOS/MBR.
Ver também
Seção intitulada “Ver também”- BIOS vs. UEFI: uma comparação mais próxima entre as duas interfaces de firmware referenciadas acima.
- Multiboot: a especificação que a maioria dos handoffs de bootloader para kernel descritos aqui de fato segue.
- A Linha A20: a peculiaridade de endereçamento que precisa ser resolvida durante as transições de modo acima.
- Unreal Mode: uma técnica que alguns bootloaders escritos à mão usam para alcançar memória acima de 1 MB antes de realizar a transição completa descrita acima.
- Boot via Rede (PXE): uma alternativa a esse estágio de bootloader sequer ser carregado de disco.