A premissa de um bootloader é literalmente, do inglês, "o que carrega a inicialização".
Esse simples projetinho foi feito em pensamento BIOS Legacy, e não UEFI mais recente, motivo será explicado.
Basicamente, ao apertar o botão de ligar,
- BIOS chama POST ( Power-On Self Test );
- POST verifica integridade dos componentes, garantindo que todos os componentes de hardware essenciais estejam funcionando corretamente.
- Verifica: Teste de memória RAM, verificando conectividade, integridade e capacidade, se está funcional, e os inicia;
- Verifica: Teste de I/O, verificando dispositivos conectados em portas de entrada e saída, se estão funcionais e os inicia;
- Verifica: Teste de Vídeo, verificando saída gráfica, se está funcional, e o inicia;
- Verifica: Teste de armazenamento, verificando HD's e SSD's, se estão funcionais, e os inicia;
- Caso de sucesso, chama MBR, verificando os primeiros 512 bytes do disco;
- MBR (Master Boot Record), o bootloader.
- 512 bytes por questão histórica, mantendo os últimos dois bytes (o último word), como
55aa, sendo a assinatura de que é boot.
- Através do MBR, se usa
interruptions(int) da BIOS para se comunicar com o hardware, carregando algumas funções necessárias.- Se utiliza destas pois o kernel ainda não está carregado, logo,
syscallsnão fazem sentido.
- Se utiliza destas pois o kernel ainda não está carregado, logo,
O motivo da escolha BIOS, e não UEFI, consta a partir do momento em que a BIOS é melhor documentada.
Ela inicia no real-mode, rodando a 16-bits por compatibilidade com o processador 8086.
Assim, sendo necessário o uso de registradores de 16-bits, como AH, AL, ...,.
Inicialmente, como dito, é necessário que o assimilador saiba que estamos mexendo com 16-bits, evitando carregar algo de maior capacidade.
- GAS (GNU ASSEMBLER)
.code16 # colocado no topo do código, primeira linha de preferência- NASM (NETWIDE ASSEMBLER)
BITS 16Como todo programa básico, precisamos setar nossa int main() como global.
No caso, puxamos da _start
.globl _start
_start:
# nosso códigoVale notar que deve ser a primeira função a ser escrita. ou seja, em sequência:
1. .code16
2. .globl _start
3. _start:
4. # código
...Ao final, devemos completar nossos 512 bytes com toda a função:
# .fill repeat, size, value
.fill 510-(.-_start), 1, 0
Setando, até 510, a partir do tamanho de _start, como 0
E os últimos 2 bytes ( WORD ), com nossa assinatura de bootloader.
.byte 0x55
.byte 0xaa