DEV Community

Cover image for Bootloader 101
Lucas Wasilewski
Lucas Wasilewski

Posted on

Bootloader 101

Olá, meu nome é Lucas Wasilewski e nos últimos três meses eu fiquei obcecado com a matéria de Sistemas Operacionais. De como o computador liga até quando a área de trabalho do Windows aparece, eu queria saber tudo, vou dizer que "diversão" definitivamente não definiu esses meses mas o conhecimento que adquiri com certeza valeu a pena.

Por mais que estejamos na era da tecnologia, onde:

Os dados são o novo petróleo

Senti que tinha entrado em um mundo diferente, onde documentação é feita por gente que programa em papel e vivem em uma caverna, onde sua única fonte de luz é um terminal preto e branco. E os tutoriais? Poucos artigos na internet ensinando o básico de assembly e dizendo o quão ele é difícil. Meu objetivo nesse artigo é encorajar, percebi que por mais que seja um assunto muito empolgante (pelo menos para mim) a maioria dos artigos e documentações desencorajam logo no inicio ou tem uma complexidade muito alta para algo que deveria ser fácil, pois, se fosse difícil ou, pior ainda, impossível, não teríamos a quantidade de sistemas operacionais que temos por aí.

Como o computador Boota

Tudo começa quando você aperta o botão de ligar do seu computador, após o click é buscado algum programa que esteja na memória RAM, como não tem nenhum então é executado um firmware que já vem de fábrica, nós o conhecemos como: BIOS.

A primeira função da BIOS é realizar um procedimento chamado POST (Power On Self Test), onde se é feito um teste nos componentes conectados para detectar algum possível problema. Vale ressaltar que esse teste não é algo exclusivo de máquinas pessoais, se é usado em praticamente todos os sistemas embarcados.

Então a BIOS agora carrega na memória da máquina um executável que possa ser bootável, e para que isso ocorra é necessário ter dois fatores:

  • Ter 512 bytes
  • Ter um assinatura de boot

Os 512 bytes são para que o bootloader tenha pelo menos um pouco de espaço para trabalhar, já a assinatura de boot são dois bytes com valores mágicos que apenas indicam que aquele é realmente o bootloader.

A função do bootloader

O bootloader é nada mais que um programa que é executado pela BIOS, sua função é coletar e especificar algumas variáveis que vão ser usadas depois pelo kernel. Também podemos dizer que seu segundo objetivo é de trocar de Modo real para o Modo protegido ou até para o Modo Longo - Modo real (16 bits) é a forma no qual o seu computador liga, imagine que ele é apenas um estado que pode fazer poucas coisas, como fazer algumas chamadas para a BIOS e acesso a alguns periféricos, também pode usar pouca memória e tem pouca segurança, ou seja, queremos sair o mais rápido possível dele. Já o Modo protegido (32 bits) é o modo no qual já temos acesso a bem mais memória e podemos ter mais liberdade para desenvolver o que bem queremos.

Seu primeiro bootloader

Certo, chega de teoria, vamos botar a mão na massa, para essa parte você vai precisar de:

  • Um Assembler (NASM)
  • Um emulador (QEMU)

Verifique como instalá-los na sua distribuição Linux ou até mesmo no Windows.

Uma rápida introdução a Assembly

O assembly é uma linguagem de montagem de mais baixo nível que temos, ela é tão próxima do computador que se usa acrônimos para traduzir código hexadecimal/binário que geram os comandos, por exemplo:

; eax = 1
mov eax, 1
Enter fullscreen mode Exit fullscreen mode

resulta em:

b8 01 00 00 00
Enter fullscreen mode Exit fullscreen mode

Onde b8 é o opcode para mov, e 01 é o próprio 1.

PS: se alguém te perguntar essa é a sintaxe da Intel

Hello World

Agora que você já consegue entender um pouco de assembly, vamos criar um arquivo chamado boot.asm, no qual vamos escrever:

[org 0x7c00]
[bits 16]

jmp $ 

times 510 - ($ - $$) db 0
dw 0xaa55
Enter fullscreen mode Exit fullscreen mode

Sim, parece bruxaria, mas vamos com calma ou melhor linha por linha.

  • [org 0x7c00]: Basicamente estamos querendo organizar a memória no qual estamos para começar a partir daquele endereço (0x7c00)
  • [bits 16]: Estamos dizendo para o nosso assembler que esse código irá rodar em um ambiente 16 bits (Modo real)
  • jmp $: Quase como um loop infinito, damos um jump para onde já estamos
  • times 510 - ($ - $$) db 0: Esse dá medo, mas a tradução seria, de onde estamos até o inicio do programa encha de 0s.
  • dw 0xaa55: define word define que os últimos 2 bytes do nosso programa contenham o número mágico (um número que a BIOS sabe que o arquivo é bootável)

Se você perceber nosso arquivo tem exatos 512 bytes de memória, ou seja estamos no limite. Para rodar basta compilar com:

$ nasm boot.asm -f bin -o boot.bin
Enter fullscreen mode Exit fullscreen mode

e rodar usando:

qemu-system-x86_64 boot.bin
Enter fullscreen mode Exit fullscreen mode

Mas, cadê o Hello World? Claro que esse programa apenas boota e faz com que a maquina fique em um estado "hibernado", não vai para frente nem para trás.
Para realmente escrevermos algo é um pouco mais complicado já que teremos que utilizar um interrupt, ou seja, vamos dizer a BIOS: "Quero escrever algo na tela" e a resposta dela é: "Ok, mas só se você já colocou tudo no lugar certo...".

[org 0x7c00]
[bits 16]


mov ah, 0x0e
mov al, 'H'
int 0x10
mov al, 'e'
int 0x10
mov al, 'l'
int 0x10
int 0x10
mov al, 'o'
int 0x10
mov al, 'W'
int 0x10
mov al, 'o'
int 0x10
mov al, 'r'
int 0x10
mov al, 'l'
int 0x10
mov al, 'd'

jmp $ 

times 510 - ($ - $$) db 0
dw 0xaa55
Enter fullscreen mode Exit fullscreen mode

O que? você achou que teríamos algo chique como print? Não caro leitor, estamos no mais baixo nível possível (quase escrevendo em binário), nosso único companheiro é a BIOS e eu não confiaria muito nela. De qualquer forma, é claro que esse código pode ser melhorado e ficar menor, mas para motivos de aprendizado, acredito que dessa forma fique de melhor compreensão.

Vamos a explicação: Sempre que chamamos int 0x10, estamos invocando a BIOS para executar uma determinada função. Você pode estar se perguntando: qual função? A resposta é que existem várias funções que ela pode executar e o valor de ah é o que determina qual função queremos executar naquele momento, então, o valor 0x0e é o código para escrever o que está armazenado em al.

Parabéns! se você chegou até aqui você escreveu seu primeiro Bootloader, claro que ele não é algo muito complexo e apenas escreve HelloWorld na tela, mas esse é seu primeiro passo e ele é o mais importante que deve ser dado.

E depois?

Bem para realmente escrever um bootloader que seja útil teríamos bem mais trabalho, teríamos que definir em que parte da memória nosso Kernel estaria, conseguir algumas informações do disco rígido, definir variáveis globais como a GDT (Global Descriptor Table) e outros. É um tópico definitivamente extenso mas que vale a pena ser estudado, é algo que pode ser alcançado e com certeza deveria ser mais difundido, acredito que na famosa "Era da tecnologia" deveríamos ter mais opções de sistemas operacionais e mais concorrência.

Quem tem um bom porquê supera qualquer como.

Referências:
https://wiki.osdev.org/Expanded_Main_Page

https://blog.ghaiklor.com/2017/10/21/how-to-implement-your-own-hello-world-boot-loader/

https://github.com/ghaiklor/ghaiklor-os-gcc

https://www.baeldung.com/cs/computer-boot-process

Top comments (0)