Gerenciador de Boot Simples

Transcrição

Gerenciador de Boot Simples
Gerenciador de Boot Simples
Este tutorial é uma simples caminhada através de um Gerenciador de Boot “Hello World”.
Este, como qualquer outro gestor, tem que obedecer a algumas regras.
Termos utilizados:
• BIOS - Um pedaço de código, que é embutido. Isto testa se o hardware está presente
e tem algumas funções embutidas que podem ser chamados usando interrupções.
• Interrupções - É uma chamada para uma função embutida(building) / procedimento
• Hexadecimal – sistema de numeração com base 16.
- Refira a números hexadecimais em Asm colocando um "h" depois ou 0x antes.
• Decimal – Sistema de numeração normal, mais conhecido. Base 10.
Regras:
1) Deve ter 512bytes de tamanho!
Isto é porque a BIOS vai automaticamente carregar o primeiro setor do disco na
memória. (Disco de inicialização está definido na BIOS, na configuração normal é
ter que arrancar a partir do drive de disquete e não que o disco rígido).
2) Deve terminar com a assinatura do bootloader, '55 AA'
Onde fica o gerenciador de inicialização carregado na memória?
Em hexadecimal, ele é carregado para 7C00.
Em decimal é carregado para 31744 (eu acho) Por que isso? Bem, o computador possui uma
série de coisas especiais armazenadas no início da memória que você ainda vai conhecer e aprender
sobre. Pessoalmente, eu só tenho uma vaga idéia do que fica entre 0 e 4864.
INT 0x10:
INT 0x10 é a interrupção de vídeo da BIOS. Todas as interrupções relacionadas a tela são
feitas com essa interrupção. Então, como você os utiliza? Bem, você tem que ter certos valores em
certos registradores para utilizá-los:
AH – 0x0E ← Teletype mode (This number must be used to tell the BIOS to put a character
on the screen)
AL – caractere ASCII para o display
BH – Número de página (Para a maior parte de nosso trabalho, será 0x00)
BL – Atributo de Texto (Para a maior parte de nosso trabalho, será 0x07) modifique este
valor para cores diferentes, etc.
Então, assim que os registros estiverem no lugar, você deve chamar a interrupção.
Instalando o boot loader:
Todos os boot loaders deste documento podem ser copiados e testador por você (exceto o
primeiro). Mas sobre sua responsabilidade.
É necessário uma CPU compatível com x86. (basicamente AMD/Intel, maioria dos Pcs
domésticos 286/286/486... Um disquete para instalar o setor de boot e o compilador NASM.
Em nosso caso, necessitaremos de um emulador de arquitetura x86 e o compilador nasm.
Os passos são:
1. Criar o arquivo assembler do boot loader e preenche-lo com o conteúdo referente;
2. Compilar com NASM, ex: “nasm arquivo.asm”;
3. Verificar se o arquivo compilado tem exatamente 512bytes;
4. Instalar o boot loader no disquete (no nosso caso, virtual):
dd if=arquivo_gerado bs=512 of=floppy.img
5. e bootar a máquina: qemu -fda floppy.img -boot a
Exemplos:
boot loader que não faz nada:
(Não copie e execute este exemplo)
; Início sessão
[BITS 16]
[ORG 0x7C00]
;
;
;
;
Diz ao compilador para fazer isso em geração de código
de 16 bits
Origem, informa ao compilador onde o código estará na
memória depois de ser carregado (em hexadecimal)
; Final sessão
times 510-($-$$) db 0
dw 0xAA55
; Preencha o resto do etor com zeros
; Adicione a assinatura ao final
Ok então o que este exemplo faz? Bem, ele não faz nada! Pelo menos nada de importante
para comentar. Deixe-me explicar, uma parte que você deve ser capaz de compreender através dos
comentários do código. Mesmo assim, o final da sessão é um pouco confuso.
times 510-($-$$) db 0
Lê-se assim: 510 vezes – (Início dessa instrução - Início do programa), com 0's
$ Representa o início da instrução
$$ Representa o início do programa
db significa definir byte - um byte é 8 bits, um bit pode ser 0 ou um 1.
dw 0xAA55
Por alguma razão, a assinatura tem de ser escrita desta forma!
Isto preenche os últimos bytes do gerenciador de inicialização com 55AA (este é um número
hexadecimal)
Sem essa assinatura, a BIOS não vai reconhecer isso como um disquete de boot!
Setor de boot em loop sem final:
[BITS 16]
; código de 16 bits
[ORG 0x7C00] ; Origem do código definida para 7C00
main:
jmp $
; label principal (Não é realmente necessário agora, porém depois)
; Salta para o início da instrução (nunca terminando o loop)
; Uma alternativa seria 'jmp main' que teria exatamente o mesmo
; efeito.
; Final da sessão
times 510-($-$$) db 0
dw 0xAA55
Portanto, agora o que isso representa? Isso coloca o carregador em um loop contínuo.
Isto é útil? Mais ou menos.
Você vai ver algo na tela? Nada... coisa de BIOS apenas
Copie este código e teste, não será interessante ainda, porém é o primeiro passo do
gerenciador de boot.
Character on the screen boot loader:
[BITS 16]
[ORG 0x7C00]
; geração de código de 16 bits
; Origem da localização 7C00
; Programa principal
main:
; principal do rótulo do programa
mov ah, 0x0E ; Este número é o número da função na BIOS para funcionar.
; Esta função é para colocar caractere na função tela
mov bh, 0x00 ; número de página
;deixe-a assim para a maioria do trabalho que vamos fazer
mov bl, 0x07 ;
;
;
;
;
mov al, 65
int 0x10
jmp $
cor do texto (Controla o fundo e o primeiro plano e
possivelmente outras opções)
07 = texto de fundo branco, preto.
(Sinta-se livre para brincar com esse valor,
pois não deve prejudicar nada)
; Isso deve (em teoria) colocar um valor em ASCII para ser
; exibido. (Este não é o caminho normal para fazer isso)
; Chamada a interrupção de vídeo da BIOS.
; Coloque-o em um loop contínuo para pará-lo correndo dentro
; da memória de execução, limpando qualquer lixo que pode encontrar lá.
; Fim da sessão
times 510-($-$$) db 0 ; Preenche o resto do setor com zeros
dw 0xAA55
; assinatura de Boot
Agora você verá um caractere aparecendo na tela.
Armazenado dados:
Se você já usou linguagem de montagem antes deste texto/código e seções de dados. Pois
bem, no carregador não temos os mesmos casos.
Então onde é que vamos colocar os dados?
Bem, nós teremos que encontrar algum lugar que não será executado como parte do
programa e colocar os dados lá (e procedimentos).
Isto é tanto no início do gerenciador de boot com uma instrução jmp usada para ignorá-los
quando quando o boot loader inicia. Ou, no final, onde nunca o gestor de arranque vai.
[BITS 16]
; geração de código de 16 bits
[ORG 0x7C00]; origem do programa. (Posição inicial)
; Programa principal
main: ; Colocando um rótulo que define o início do programa principal
call PutChar ; Execute o procedimento
jmp $
; Coloque o programa em um ciclo interminável
; Tudo aqui está fora do programa principal
; Procedimentos
PutChar:
mov ah, 0x0E
mov bh, 0x00
mov bl,0x07
mov al,65
int 0x10
ret
;
;
;
;
;
;
;
Label do procedimento
Coloque o número da função char (Elevado)
número de página (Ignorar por enquanto)
atributo normal
código de caracteres ASCII
Interrupção de execução
Voltar ao programa principal
; Esta informação nunca é executada, nem mesmo como um procedimento
; Dados
TestHugeNum dd 0x00
TestLargeNum dw 0x00
TestSmallNum db 0x00
;
;
;
;
;
;
Este pode
Até o hex
Isso pode
Até o hex
Este pode
Até o hex
ser um número muito grande (1 palavra dupla)
ffffffff
ser um bom número bem grande (1 palavra)
ffff
ser um número pequeno (1 byte)
ff
TestString db 'Test String',13,10,0
; Final da sessão
times 510-($-$$) db 0
dw 0xAA55
;Esta é uma string (pode ser bem longa)
; zero para o resto do setor
; assinatura Bootloader
Execute este código de boot também.
O que ele faz? Exatamente o mesmo de antes, colocando alguns valores na memória, e
colocando o código responsável por um caractere na tela por um procedimento.
A principal coisa que vai olhar incomum é a linha: TestString db 'Test String ", 13,10,0
Como isso é apenas um db (byte)?
Bem, não é, mas TestString armazena apenas a localização de memória e não os dados. E a
referência de localização da string pode ser armazenada em um byte.
O que acontece com os números no final?
13 – ASCII para Retorno
10 – ASCII para Nova Linha
(Os dois juntos fazem iniciar o texto seguinte na linha seguinte)
0 – Não faz nada, mas será mais tarde utilizado como um marcador para o fim da cadeia
O resto do código que você deve compreender a partir de seus comentários.
Escrevendo toda a string:
[BITS 16]
[ORG 0x7C00]
; geração de código de 16 bits
; local de origem
; Programa Principal
main:
; Label para o programa principal
mov ax,0x0000
mov ds,ax
;
;
;
;
;
configuração dos dados do registo do segmento
Localização dos dados é DS: Offset
Isto não pode ser carregado diretamente,
tem que ser em 2 passos
'mov ds, 0x0000' não irá funcionar devido a limitações na CPU
mov si, HelloWorld
; Carrega a string dentro da posição para o procedimento
call PutStr
; Chama/Inicia o procedimento
jmp $
; loop sem final
; Procedimentos
PutStr:
; Configura os
mov ah,0x0E
mov bh,0x00
mov bl,0x07
; Label/Início do Procedimento
registros para a chamada de interrupção
; A função para mostrar o caractere (teletype)
; Número de página
; Atributo normal de texto
.nextchar
lodsb
; Label interno
; (necessário para continuar o loop para o próximo caractere)
; LOaD String Block
; Checa pelo final 0 da string
or al,al
; Define zero para a bandeira se al = 0
jz .return
int 0x10
jmp .nextchar
.return
ret
;
;
;
;
;
;
;
Se o sinalizador zero foi definido ir até o fim do processo.
bandeira zero fica definido quando uma instrução
retorna 0 como resposta.
Executa a interrupção de vídeo na BIOS
Voltar o ciclo ao topo
Label para o final do loop
Retorna para o programa principal
; Dados
HelloWorld db 'Hello World',13,10,0
; Final da sessão
times 510-($-$$) db 0
dw 0xAA55
; Preenche o resto com zeros
; Assinatura do Boot loader
Ok, agora isso vai colocar 'Olá Mundo' na tela.
Fonte: <http://www.osdever.net/tutorials/hello_btldr.php>
Autor: Daniel Rowell Faulkner
Tradução e Adaptação: Jacson RC Silva