GilgaLab

2Nov/076

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

Posted by Henrique

Comments (6) Trackbacks (0)
  1. Cara… tô sem fôlego… quando eu crescer quero fazer isso aí…

    meu, embasbaquei… tá de parabéns…

  2. 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?

  3. 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?

  4. 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

  5. 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 :)

  6. Ótimas explicações!!!!

    Parabéns!

    “Quem abre a mente reduz o mundo!” ( NeoqJav )


Leave a comment

(required)

No trackbacks yet.