Bootstrap Simples
Introdução
Esse fim de semana andei estudando um pouco de assembly. Decidi fazer um bootstrap para ver se me animava um pouco, e acabou me voltando a vontade de realmente brincar de criar o meu sistema operacional. Como tudo precisa de um começo, decidi que o bootstrap seria um belo lugar para começar.
Vejamos então como é que fazemos para dar um boot na maquina e o que é que a(o) BIOS (Basic Input Output System) espera para iniciar o sistema operacional.
Quando o computador inicia e o POST termina, a BIOS faz uma chamada para ler o primeiro setor do disco que está definido como primário no setup. Funciona assim:
a) Olha o primeiro setor do disco definido no setup
b) Encontrou setor de boot válido?
c) Sim. Lê o setor e carrega o código para o segmento endereço 7C00h na memória. (Valeu pela correção Muzgo :)
d) Não. Parte para o próximo dispositivo da lista de boot e vai para a) até encontrar.
e) Caso não encontre em nenhum dos dispositivos, exibe mensagem de erro padrão da BIOS.
A Lista de software que usei:
1. NASM - Para compilar o código
2. QEMU - Maquina virtual que uso para testar o setor de boot
3. dd - Utilizado para escrever os dados no disco
O setor de boot
Quando definimos o disco no qual desejamos dar o boot, a BIOS chama uma interrupção (19h se nao me engano), para ler o primeiro setor desse disco. O primeiro setor é o que se encontra na posição CHS (Cylinder Head Sector) 0:0:1. Cada setor no disco tem 512 bytes, então como a BIOS lê o primeiro setor do disco, nosso 'programa' precisa ter 512 bytes ou menos. Meu objetivo aqui não é de falar muito sobre HCS, maiores informações você pode encontrar aqui.
O que identifica se temos um setor de boot válido ou não?
O setor de boot tem uma 'assinatura', os últimos dois bytes do setor devem ser '0xAA55'. Quando essa assinatura é encontrada, os 512 bytes são carregados para a memória na posição 7C00h e o programa é executado.
O código
Como já diria o conde Drácula em 'Castlevania: Symphony of the Night': "Enough talk!".
Vamos dar uma olhada então em um código bem simples, que imprime uma string na tela.
; Boot.asm ORG 7C00h ; Posição onde estaremos quando o código for ; carregado para a memória mymsg: db 'Olá Setor de BOOT',10,0 xor ax,ax ; Limpando ax mov si,mymsg ; Nossa mensagem em SI putstr: lodsb ; Coloca o byte apontado em SI em AL, e incrementa ; o contador or al,al ; Verificamos se encontramos o byte '0' da string jz hang; ; Se sim, paramos de imprimir mov ah,0x0E ; Função para escrever mov bx,0x0007 ; Define a página e a cor onde escrevemos int 0x10 ; Interrupção de video jmp putstr ; Imprimir proximo caracter hang: jmp hang ; Após impressão, entramos em loop infinito times 512-($-$$)-2 DB 0 ; Preenchemos o resto da memória com '0's ; até 510 bytes DW 0xAA55 ; Assinatura do setor de boot
Acredito que o código esteja auto explicativo. Você deve precisar manjar um pouco de assembly, então se não entendeu o código, procure estudar um pouquinho de assembly.
Vamos agora compilar o nosso código, criar um disco para o QEMU, e escrever o nosso setor de boot no disco.
$ nasm boot.asm -f bin -o boot.bin
$ qemu-img create /tmp/boot.img -f qcow 1M
$ dd if=boot.bin of=/tmp/boot.img
Com isso feito, basta agora executar o QEMU e dizer que desejamos usar o arquivo /tmp/boot.img como nosso disco.
$ qemu /tmp/boot.img -m 16
O resultado é a nossa frase impressa logo após a BIOS fazer o POST.
Colocando código no Disco
Bom, agora que já sabemos dar o boot, está na hora de colocarmos código no disco e executar esse código. Esse é um processo um pouco mais complicado, mas vamos que vamos!
A idéia agora é fazer com que o nosso programa de boot chame um binario que esteja gravado no disco e o execute. Então precisaremos escrever dois programas distintos: Um para ser o nosso setor de boot, e um que será chamado por ele. "Show me the code"
; Boot2.asm ORG 7C00h mov si,msg prntMsg: ; Imprime a mensagem em SI lodsb mov ah,0x0E mov bx,0x0007 int 10h or al,al jnz prntMsg mov [drv],dl ; DL contém o identificador da unidade em que ; o setor de boot foi encontrado ; Inicializar o disco. Aqui colocamos a cabeça do disco no inicio dele .diskSetup mov ax,0 ; Função para resetar o disco rígido mov dl,[drv] ; O drive que vamos resetar int 13h ; Chama a interrupção de disco jc .diskSetup ; Se der erro, tentamos de novo ; Após resetar o disco, colocaremos a cabeça no setor que o programa se ; encontra e carregamos o programa na memória .diskRead mov ah,02h ; Função para ler o disco mov al,3 ; Ler 3 setores (512 * 3 bytes) mov ch,0 ; Apontar para o cilindro 0 mov cl,0x02 ; Ler a partir do setor 2 (2, 3 e 4) mov dh,0 ; Cabeça 0 mov dl,[drv] ; Disco de onde queremos ler os dados ; Os dados que lemos com essa função são armazenados em ES:BX ; No nosso caso aqui entao teremos 1000h:0 mov bx,0x1000 mov es,bx mov bx,0 int 13h ; Interrupção do disco jc .diskRead ; Em caso de erro, tenta de novo jmp 1000h:0; ; Aqui nós pulamos para o nosso código que acaba de ser ; carregado na memória hang: jmp hang ; Loop infinito drv db 0 msg db 'Chamando programa do HD',13,10,0 times 512-($-$$)-2 db 0 ; Completa o espaço do setor que sobra com 0s DW 0xAA55 ; Assinatura do setor de boot nos ultimos dois bytes
Esse código então, como da para perceber, lê o código do disco, coloca o código na posição de memória 1000h:0 e pula para lá para começar a execução. Bom, precisamos agora então do programa que desejamos ler do disco e executar. Esse programa, assim como o primeiro exemplo vai simplesmente imprimir uma mensagem na tela.
; Programa.asm mov ax, 1000h ; Atualizar os registros de segmentos mov ds, ax mov es, ax mov si, msg ; Mensagem em SI putstr: lodsb or al,al jz hang mov ah,0x0E mov bx,0x0007 int 0x10 jmp putstr hang: jmp hang msg db 'Bla bla bla!',13,10,0
Nesse programa nós atualizamos os registros de segmento para o mesmo endereço onde carregamos o programa do nosso HD, e executamos a rotina basica de imprimir a mensagem na tela, e entrar em loop infinito.
Para executar esse exemplo, os paso são parecidos. Vejamos:
$ nasm boot2.asm -f bin -o boot2.bin
$ nasm programa.asm -f bin -o programa.bin
$ qemu-img create ./boot2.img -f qcow 1M
$ dd if=boot2.bin of=./boot2.img
$ dd if=programa.bin of=./boot2.img bs=512 seek=1
$ qemu ./boot.img -m 16
O comando '$ dd if=programa.bin of=./boot2.img bs=512 seek=1' coloca o nosso código no segundo setor do disco, pois conforme definimos em nosso código, é la que nosso bootstrap está esperando encontra-lo.
Conclusão
Bom pessoal, é isso ai. Como o tpitulo disse, é um bootstrap simples, apenas para dar um gostinho de como funciona, e trazer mais animo para maiores pesquisas.
Espero que tenham gostado e que possa ser útil para alguém.
Até o próxmo :D
March 6th, 2008 - 14:55
Cara… tô sem fôlego… quando eu crescer quero fazer isso aí…
meu, embasbaquei… tá de parabéns…
March 29th, 2008 - 21:15
Gostei muito do texto, bem didatico. Em 2003 eu fiz meu micro sistema operacional composto de um bootloader e de um Kernel, ambos em assembly. Agora eu quero fazer a mesma coisa, só que o Kernel eu quero fazer utilizando a linguagem C em modo real. Você já tentou usar C?
March 30th, 2008 - 15:58
Fala Fabio,
Cara nao tentei fazer em C ainda. Na verdade, depois que escrevi esse texto, não tive mais tempo de fuçar nesse tipo de coisa. Voce ainda tem o codigo desse seu micro S.O?
May 2nd, 2008 - 10:35
Cara…
bom de mais para quem entende >>
para mim e só um monte de coisas escritas que não tem fundamento nenhum.
Comecei a me interessar por assembly ja tem tempo >> mais de um ano.
porém ate hoje tudo que faço e venerar a linguagem pois tudo que sei e um monte de instrução que não faço a minima de que é e para que serve.
Tutorias assembly na internet e coisa dificel
Todos os tutoriais falam em uma linguagem muito tecnica que dificulta muito o entendimento.
Gostaria de saber se vc conhece algum tutorial assembler realmente bom que nao encine a fazer chamada a “mandataria” API do win (encontrei tutorial assembly muito bom falando sobre chamadas a API).
Gostaria de trabalhar direto com o pc e me aventurar nessa de fazer um OS :)
estou com um pc aqui esperando para ser estragado e consertado nessa intenção de estudo.
Vlw
May 2nd, 2008 - 19:33
Um tanto quanto difícil isso. Assembly é uma linguagem que varia de plataforma para plataforma. Se está pretendendo ver algo a respeito de programação direto com o hardware, como para desenvolver um OS, existem alguns bons textos por ai explicando cada parte de um sistema operacional.
Voce pode consultar alguns livros tais como:
Sistemas Operacionais Modernos – Andrew S Tanenbaum
Sistemas Operacionais: Projeto e Implementação – ALBERT S. WOODHULL &ANDREW S. TANENBAUM
Cove pode consultar os seguintes links:
http://www.osdev.org/
http://www.osdever.net/tutorials.php?cat=0&sort=1
Pode ler a documentação do kernel do linux, para entender como um kernel funciona. Pode ler os manuais da intel para entender como funcionam os conjuntos de instruções dos processadores ia32.
Acredito que esse seja o caminho.
Bons estudos :)
April 8th, 2011 - 15:22
Ótimas explicações!!!!
Parabéns!
“Quem abre a mente reduz o mundo!” ( NeoqJav )