Oficina
6. O setor de boot
Sab 26 Mai 2007 14:48 |
- Detalhes
- Categoria: Sistemas Operacionais
- Atualização: Domingo, 12 Abril 2009 12:30
- Autor: vovó Vicki
- Acessos: 16722
MÓDULO 6 do SO Numaboa
Todo sistema operacional precisa ser acionado quando o computador é ligado ou reiniciado (ressetado). O hardware da máquina já vem preparado "de fábrica" para procurar informações em determinados lugares - os assim chamados setores de boot.
Antes de iniciar qualquer projeto de SO, sabendo de antemão que o setor de boot será o primeiro que a máquina procura, é preciso ter uma idéia clara do que é um setor de boot. Neste tutorial você encontra uma verdadeira radiografia do setor de boot de um disquete formatado pelo DOS. Parto do pressuposto que você tenha conhecimentos suficientes de Assembly para acompanhar o texto.
Se os seus conhecimentos de Assembly forem insuficientes, ainda assim vale a pena dar uma lida no texto. Imagine que o setor de boot seja uma sequência de bits localizados logo no início do disquete. Esta sequência de bits é dividida em algumas partes e cada uma delas contém determinados tipos de dados e informações.
Onde está o setor de boot
O setor de boot de um disquete está localizado no cilindro 0, cabeça 0, setor 1. Este setor é criado por um programa de formatação de disquetes, como o FORMAT do DOS. O setor de boot de uma partição FAT de um disco rígido tem um layout e função semelhantes. Uma partição FAT "bootável" se parece com um disquetão durante os estágios iniciais do processo de boot do sistema.
O processo de boot
Quando o computador é ligado ou reiniciado, a BIOS executa um teste conhecido como POST (Power On Self Test). O POST verifica toda a memória, testa todas as placas e o hardware é rastreado. É através do POST que o computador é informado do número e dos tipos de placas, de drives de disquete, de HDs, de portas seriais, paralelas e USBs, do monitor, do mouse, do teclado, etc.
Após o sistema completar o POST, a interrupção 19 (INT 19) é chamada. Normalmente a INT 19 tenta ler o setor de boot do primeiro drive de disquete. Se for encontrado um setor de boot no disquete, este é lido e transferido para a memória no endereço 0000:7C00 e a INT 19 salta para o endereço de memória 0000:7C00. Entretanto, se não for encontrado um setor de boot no primeiro drive, a INT 19 tenta fazer a leitura do MBR (Master Boot Record) do primeiro disco rígido. Se um MBR for encontrado, ele é lido e transferido para a localização de memória 0000:7C00 e a INT 19 salta para o endereço de memória 0000:7C00. O pequeno programa no MBR tentará localizar uma partição ativa ("bootável") na sua tabela de partições. Se uma partição "bootável" for encontrada, o setor de boot desta partição é lida e transferida para o endereço de memória 0000:7C00 e o programa do MBR salta para o endereço de memória 0000:7C00. Cada sistema operacional possui um formato de setor de boot próprio. O pequeno programa no setor de boot precisa localizar a porção inicial do programa carregador (loader) do kernel do sistema operacional (ou, talvez, o próprio kernel ou um "programa gerenciador de boot") e transferi-lo para a memória.
A INT 19 também é chamada quando as teclas Ctrl+Alt+Del são pressionadas simultaneamente. Na maioria dos sistemas, Ctrl+Alt+Del disparam uma versão reduzida do POST, que é executada antes da chamada à INT 19.
Dissecando o setor de boot
Para começar, observe logo abaixo o setor de boot de um disquete mostrado em hexadecimal e em ASCII. Os deslocamentos destacados em vermelho mostram o início das principais áreas:
- O BPB (BIOS Parameter Block) começa no deslocamento (offset) 0.
- O programa do setor de boot começa no deslocamento 3E.
- As mensagens fornecidas pelo programa começam no deslocamento 19E.
- O nomes dos arquivos ocultos do DOS começam no deslocamento 1E6.
- A assinatura do setor de boot está no deslocamento 1FE.
OFFSET 0 1 2 3 4 5 6 7 8 9 A B C D E F *0123456789ABCDEF* 000000 eb3c904d 53444f53 352e3000 02010100 *...MSDOS5.0.....* 000010 02e00040 0bf00900 12000200 00000000 *...@............* 000020 00000000 0000295a 5418264e 4f204e41 *......)ZT..NO NA* 000030 4d452020 20204641 54313220 2020fa33 *ME FAT12 .3* 000040 c08ed0bc 007c1607 bb780036 c5371e56 *.....|...x.6.7.V* 000050 1653bf3e 7cb90b00 fcf3a406 1fc645fe *.S..|.........E.* 000060 0f8b0e18 7c884df9 894702c7 073e7cfb *....|.M..G....|.* 000070 cd137279 33c03906 137c7408 8b0e137c *..ry3.9..|t....|* 000080 890e207c a0107cf7 26167c03 061c7c13 *.. |..|...|...|.* 000090 161e7c03 060e7c83 d200a350 7c891652 *..|...|....P|..R* 0000a0 7ca3497c 89164b7c b82000f7 26117c8b *|.I|..K|. ....|.* 0000b0 1e0b7c03 c348f7f3 0106497c 83164b7c *..|..H....I|..K|* 0000c0 00bb0005 8b16527c a1507ce8 9200721d *......R|.P|...r.* 0000d0 b001e8ac 0072168b fbb90b00 bee67df3 *.....r........}.* 0000e0 a6750a8d 7f20b90b 00f3a674 18be9e7d *.u... .....t...}* 0000f0 e85f0033 c0cd165e 1f8f048f 4402cd19 *._.3...^....D...* 000100 585858eb e88b471a 48488a1e 0d7c32ff *XXX...G.HH...|2.* 000110 f7e30306 497c1316 4b7cbb00 07b90300 *....I|..K|......* 000120 505251e8 3a0072d8 b001e854 00595a58 *PRQ.:.r....T.YZX* 000130 72bb0501 0083d200 031e0b7c e2e28a2e *r..........|....* 000140 157c8a16 247c8b1e 497ca14b 7cea0000 *.|..$|..I|.K|...* 000150 7000ac0a c07429b4 0ebb0700 cd10ebf2 *p....t).........* 000160 3b16187c 7319f736 187cfec2 88164f7c *;..|s..6.|....O|* 000170 33d2f736 1a7c8816 257ca34d 7cf8c3f9 *3..6.|..%|.M|...* 000180 c3b4028b 164d7cb1 06d2e60a 364f7c8b *.....M|.....6O|.* 000190 ca86e98a 16247c8a 36257ccd 13c30d0a *.....$|.6%|.....* 0001a0 4e6f6e2d 53797374 656d2064 69736b20 *Non-System disk * 0001b0 6f722064 69736b20 6572726f 720d0a52 *or disk error..R* 0001c0 65706c61 63652061 6e642070 72657373 *eplace and press* 0001d0 20616e79 206b6579 20776865 6e207265 * any key when re* 0001e0 6164790d 0a00494f 20202020 20205359 *ady...IO SY* 0001f0 534d5344 4f532020 20535953 000055aa *SMSDOS SYS..U.*
O BPB (BIOS Parameter Block)
Os primeiros 62 bytes de um setor de boot são conhecidos como BPB (BIOS Parameter Block - Bloco de Parâmetros da BIOS). Lembre-se de que o conteúdo do setor de boot é transferido para o endereço de memória 0000:7C00. Analisando o setor de boot mostrado anteriormente, o BPB mostra o seguinte:
7C00 eb3c db instrução JMP 7C02 90 db instrução NOP 7C03 'MSDOS5.0' db OEMname 7C0B 0200 dw bytesPerSector; 00 02 -> 02 00 7C0D 01 db sectPerCluster 7C0E 0001 dw reservedSectors; 01 00 -> 00 01 7C10 02 db numFAT 7C11 00e0 dw numRootDirEntries; e0 00 -> 00 e0 7C13 0b40 dw numSectors; ignorar numSectorsHuge 7C15 f0 db mediaType 7C16 0009 dw numFATsectors 7C18 0012 dw sectorsPerTrack 7C1A 0002 dw numHeads 7C1C 00000000 dd numHiddenSectors 7C20 00000000 dd numSectorsHuge 7C24 00 db driveNum 7C25 00 db reserved 7C26 29 db signature 7C27 5a541826 dd volumeID 7C2B 'NO NAME ' db volumeLabel 7C36 'FAT12 ' db fileSysType
A Tabela de Parâmetros do Disquete
Os 11 bytes com início em 0000:7C3E são imediatamente sobrepostos com informações copiadas de outra parte da memória. Esta informação é a Tabela de Parâmetros do Disquete. Estes dados são apontados pela INT 1E e são os seguintes:
7C3E = Taxa de passo (step rate) e tempo de descarga da cabeça (head unload time). 7C3F = Tempo de carga da cabeça (head load time) e flag de modo do DMA. 7C40 = Retardo para o motor desligar. 7C41 = Bytes por setor. 7C42 = Setores por trilha. 7C43 = Comprimento da lacuna entre os setores. 7C44 = Comprimento dos dados. 7C45 = Comprimento da lacuna entre os setores durante a formatação. 7C46 = Valor do byte da formatação. 7C47 = Tempo do ajuste da cabeça (head settling time). 7C48 = Retardo até que o motor atinja a velocidade normal.
Os 11 bytes seguintes, com início em 0000:7C49, também são sobrepostos pelos seguintes dados:
7C49 - 7C4C = endereço do setor do disquete (como LBA) da área de dados. 7C4D - 7C4E = número do cilindro onde deve ser feita a leitura. 7C4F - 7C4F = número do setor onde dever ser feita a leitura. 7C50 - 7C53 = endereço do setor do disquete (como LBA) do diretório raiz.
O programa do setor de boot desassemblado
A seguir, o código em Assembly do programa do setor de boot. É um tanto extenso, mas vale a pena dar uma olhada. Não se preocupe se não identificar todas as instruções utilizadas. À medida que avançarmos nos tutoriais, elas serão referenciadas.
INICIO: INÍCIO DO PROGRAMA DO SETOR DE BOOT 0000:7C3E FA CLI desliga as interrupções 0000:7C3F 33C0 XOR AX,AX zera AX 0000:7C41 8ED0 MOV SS,AX SS agora é zero 0000:7C43 BC007C MOV SP,7C00 SP agora é 7C00 0000:7C46 16 PUSH SS põe zero na pilha 0000:7C47 07 POP ES e zera ES O vetor da INT 1E etá em 0000:0078. Pega o endereço apontado pelo vetor e o coloca nos registradores DS:SI. 0000:7C48 BB7800 MOV BX,0078 BX agora é 78 0000:7C4B 36 SS: 0000:7C4C C537 LDS SI,[BX] DS:SI agora é [0:78] 0000:7C4E 1E PUSH DS salva DS:SI -- 0000:7C4F 56 PUSH SI salva end tab param 0000:7C50 16 PUSH SS salva SS:BX -- 0000:7C51 53 PUSH BX salva end da INT 1E Move a tabela de parâmetros do disquete para 0000:7C3E. 0000:7C52 BF3E7C MOV DI,7C3E DI é o endereço de START 0000:7C55 B90B00 MOV CX,000B contador é 11 0000:7C58 FC CLD limpa a direção 0000:7C59 F3 REPZ move a tabela de parâmetros 0000:7C5A A4 MOVSB do disquete para 0000:7C3E 0000:7C5B 06 PUSH ES também zera 0000:7C5C 1F POP DS DS Altera alguns dados da tabela de parâmetros do disquete. 0000:7C5D C645FE0F MOV BYTE PTR [DI-02],0F muda o tempo de ajuste da cabeça em 0000:7C47 0000:7C61 8B0E187C MOV CX,[7C18] setores por trilha 0000:7C65 884DF9 MOV [DI-07],CL salva em 0000:7C42 Muda INT 1E para que aponte para a tabela de parâmetros do disquete alterada em 0000:7C3E 0000:7C68 894702 MOV [BX+02],AX muda segmento da INT 1E 0000:7C6B C7073E7C MOV WORD PTR [BX],7C3E muda deslocamento da INT 1E Chama INT 13 com AX=0000, reset de disco, de modo que a nova tabela de parâmetros do disquete seja usada. 0000:7C6F FB STI liga as interruções 0000:7C70 CD13 INT 13 faz chamada do reset do disquete 0000:7C72 7279 JB TALK salta se houver erro Detemina o endereço inicial do setor do diretório raiz como um LBA. 0000:7C74 33C0 XOR AX,AX AX agora é zero 0000:7C76 3906137C CMP [7C13],AX néumero de setores zero? 0000:7C7A 7408 JZ SMALL_DISK sim 0000:7C7C 8B0E137C MOV CX,[7C13] número de setores 0000:7C80 890E207C MOV [7C20],CX salva em número gigante DISCO_PEQUENO: 0000:7C84 A0107C MOV AL,[7C10] número de tabelas FAT 0000:7C87 F726167C MUL WORD PTR [7C16] número de setores FAT 0000:7C8B 03061C7C ADD AX,[7C1C] nro de setores escondidos 0000:7C8F 13161E7C ADC DX,[7C1E] nro de setores escondidos 0000:7C93 03060E7C ADD AX,[7C0E] nro de setores reservados 0000:7C97 83D200 ADC DX,+00 nro de setores reservados 0000:7C9A A3507C MOV [7C50],AX salva endereço inicial 0000:7C9D 8916527C MOV [7C52],DX do dir raiz (como LBA) 0000:7CA1 A3497C MOV [7C49],AX salva endereço inicial 0000:7CA4 89164B7C MOV [7C4B],DX do dir raiz (como LBA) Determina o endereço de setor do primeiro setor na área de dados como um LBA. 0000:7CA8 B82000 MOV AX,0020 tamanho de uma entrada de diretório (32) 0000:7CAB F726117C MUL WORD PTR [7C11] nro de entradas do diretório raiz 0000:7CAF 8B1E0B7C MOV BX,[7C0B] bytes por setor 0000:7CB3 03C3 ADD AX,BX 0000:7CB5 48 DEC AX 0000:7CB6 F7F3 DIV BX 0000:7CB8 0106497C ADD [7C49],AX soma ao endereço inicial 0000:7CBC 83164B7C00 ADC WORD PTR [7C4B],+00 do dir raiz (como LBA) Transfere o primeiro setor do diretório raiz para 0000:0500. 0000:7CC1 BB0005 MOV BX,0500 end para transferência 0000:7CC4 8B16527C MOV DX,[7C52] pega end inicial do 0000:7CC8 A1507C MOV AX,[7C50] dir raiz (como LBA) 0000:7CCB E89200 CALL CONVERT chama rotina de conversão 0000:7CCE 721D JB TALK salta se houver erro 0000:7CD0 B001 MOV AL,01 ler 1 setor 0000:7CD2 E8AC00 CALL READ_SECTORS lê primeiro setor do diretório raiz 0000:7CD5 7216 JB TALK salta se houver erro 0000:7CD7 8BFB MOV DI,BX end da primeira entrada do diretório 0000:7CD9 B90B00 MOV CX,000B contador é 11 0000:7CDC BEE67D MOV SI,7DE6 end dos nomes de arquivos 0000:7CDF F3 REPZ é o "IO.SYS"? 0000:7CE0 A6 CMPSB 0000:7CE1 750A JNZ TALK não 0000:7CE3 8D7F20 LEA DI,[BX+20] end da próxima entrada do diretório 0000:7CE6 B90B00 MOV CX,000B contador é 11 0000:7CE9 F3 REPZ é o "MSDOS.SYS"? 0000:7CEA A6 CMPSB 0000:7CEB 7418 JZ FOUND_FILES são iguais FALA: Mostra mensagem "Non-System disk...", espera usuário apertar uma tecla, restaura o vetro da INT 1E e depois chama a INT 19 para iniciar o processo de boot novamente. 0000:7CED BE9E7D MOV SI,7D9E "Non-System disk..." 0000:7CF0 E85F00 CALL MSG_LOOP mostra mensagem 0000:7CF3 33C0 XOR AX,AX função INT 16 0000:7CF5 CD16 INT 16 lê teclado 0000:7CF7 5E POP SI pega o endereço do 0000:7CF8 1F POP DS vetor da INT 1E 0000:7CF9 8F04 POP [SI] restaura os dados do 0000:7CFB 8F4402 POP [SI+02] vetor da INT 1E 0000:7CFE CD19 INT 19 chama INT 19 para tentar novamente PREPARA_FALA: 0000:7D00 58 POP AX tira lixo da pilha 0000:7D01 58 POP AX tira lixo da pilha 0000:7D02 58 POP AX tira lixo da pilha 0000:7D03 EBE8 JMP TALK agora fala com o usuário ARQUIVOS_ENCONTRADOS: Trata o endereço de setor do primeiro setor de IO.SYS. 0000:7D05 8B471A MOV AX,[BX+1A] pega nro do cluster inicial 0000:7D08 48 DEC AX subtrai 1 0000:7D09 48 DEC AX subtrai 1 0000:7D0A 8A1E0D7C MOV BL,[7C0D] setores por cluster 0000:7D0E 32FF XOR BH,BH 0000:7D10 F7E3 MUL BX multiplica 0000:7D12 0306497C ADD AX,[7C49] adiciona o end inicial 0000:7D16 13164B7C ADC DX,[7C4B] do dir raiz (como LBA) Transfere IO.SYS para a memória em 0000:0700. IO.SYS tem comprimento de 3 setores. 0000:7D1A BB0007 MOV BX,0700 end de transferência 0000:7D1D B90300 MOV CX,0003 lê 3 setores LOOP_DE_LEITURA: Lê os primeiros 3 setores de IO.SYS (IO.SYS muito mais do que 3 setores). 0000:7D20 50 PUSH AX salva AX 0000:7D21 52 PUSH DX salva DX 0000:7D22 51 PUSH CX salva CX 0000:7D23 E83A00 CALL CONVERT chama rotina de conversão 0000:7D26 72D8 JB SETUP_TALK salta se houver erro 0000:7D28 B001 MOV AL,01 lê um setor 0000:7D2A E85400 CALL READ_SECTORS lê um setor 0000:7D2D 59 POP CX restaura CX 0000:7D2E 5A POP DX restaura DX 0000:7D2F 58 POP AX restaura AX 0000:7D30 72BB JB TALK salta se erro INT 13 0000:7D32 050100 ADD AX,0001 soma 1 ao end do setor 0000:7D35 83D200 ADC DX,+00 soma 1 ao end do setor 0000:7D38 031E0B7C ADD BX,[7C0B] incrementa o end de mem com tamanho do setor 0000:7D3C E2E2 LOOP READ_LOOP lê próximo setor Deixa informação nos registradoresAX, BX, CX e DX para uso do IO.SYS. Finalmente, salta para o IO.SYS em 0070:000. 0000:7D3E 8A2E157C MOV CH,[7C15] tipo de mídia 0000:7D42 8A16247C MOV DL,[7C24] número do drive 0000:7D46 8B1E497C MOV BX,[7C49] pega o end inicial do 0000:7D4A A14B7C MOV AX,[7C4B] dir raiz (como LBA) 0000:7D4D EA00007000 JMP 0070:0000 salta para 0070:0000 LOOP_MENSAGEM: Esta rotina mostra uma mensagem usando a INT 10, um caracter por vez. O endereço da mensagem está em DS:SI. 0000:7D52 AC LODSB pega caracter da msg 0000:7D53 0AC0 OR AL,AL fim da mensagem? 0000:7D55 7429 JZ RETURN salta se sim 0000:7D57 B40E MOV AH,0E mostra um caracter 0000:7D59 BB0700 MOV BX,0007 atributos de vídeo 0000:7D5C CD10 INT 10 mostra um caracter 0000:7D5E EBF2 JMP MSG_LOOP faça novamente CONVERTE: Esta rotina converte um endereço de setor (um LBA) num endereço CHS. O LBA está em DX:AX. 0000:7D60 3B16187C CMP DX,[7C18] parte alta do LBA maior que sectPerTrk? 0000:7D64 7319 JNB SET_CARRY salta se sim 0000:7D66 F736187C DIV WORD PTR [7C18] divide por setores por trilha 0000:7D6A FEC2 INC DL soma 1 ao nro de setores 0000:7D6C 88164F7C MOV [7C4F],DL salva número de setores 0000:7D70 33D2 XOR DX,DX zera DX 0000:7D72 F7361A7C DIV WORD PTR [7C1A] div número de cabeças 0000:7D76 8816257C MOV [7C25],DL salva número de cabeças 0000:7D7A A34D7C MOV [7C4D],AX salva nro de cilindros 0000:7D7D F8 CLC limpa carry 0000:7D7E C3 RET retorna SET_CARRY: 0000:7D7F F9 STC set carry RETORNA: 0000:7D80 C3 RET retorna LER_SETORES: O chamador desta rotina fornece: AL = número de setores a serem lidos ES:BX = localização da mem para transferência e CHS endereço da leitura a ser transferida para as localizações de memória 7C25 e 7C4D a 7C4F. 0000:7D81 B402 MOV AH,02 INT 13 lê setores 0000:7D83 8B164D7C MOV DX,[7C4D] pega nro de cilindros 0000:7D87 B106 MOV CL,06 shift contador 0000:7D89 D2E6 SHL DH,CL shift esq. cilindro mais significativo 6 bits 0000:7D8B 0A364F7C OR DH,[7C4F] OR no número do setor 0000:7D8F 8BCA MOV CX,DX move para CX 0000:7D91 86E9 XCHG CH,CL CH=cilindro baixo, CL=cilindro alto + setor 0000:7D93 8A16247C MOV DL,[7C24] número do drive 0000:7D97 8A36257C MOV DH,[7C25] número da cabeça 0000:7D9B CD13 INT 13 lê setores 0000:7D9D C3 RET retorna
Seguem alguns dados não usados:
0000:7D90 ca86e98a 16247c8a 36257ccd 13c3.... *.....$|.6%|... *
A mensagem do programa
0000:7D90 ........ ........ ........ ....0d0a * ..* 0000:7Da0 4e6f6e2d 53797374 656d2064 69736b20 *Non-System disk * 0000:7Db0 6f722064 69736b20 6572726f 720d0a52 *or disk error..R* 0000:7Dc0 65706c61 63652061 6e642070 72657373 *eplace and press* 0000:7Dd0 20616e79 206b6579 20776865 6e207265 * any key when re* 0000:7De0 6164790d 0a00.... ........ ........ *ady... *
Nomes dos arquivos ocultos
0000:7De0 ........ ....494f 20202020 20205359 * IO SY* 0000:7Df0 534d5344 4f532020 20535953 000055aa *SMSDOS SYS..U.*
A assinatura do setor de boot
Os dois últimos bytes contém a assinatura do setor de boot, 55 AA:
0000:7Df0 ........ ........ ........ ....55aa * U.*
Considerações finais
Se você conseguiu acompanhar o texto, está na hora de fazer alguma pesquisa. Pegue um editor hexadecimal, dê uma olhada em alguns disquetes, "bootáveis" ou não, e compare os setores de boot. Eu costumo usar o editor WinHex pois, além de outras facilidades, permite visualizar diretamente os setores de discos.
Quero agradecer de público a Hale Landis, especialista em interfaces para dispositivos de armazenamento de dados, autor de vários artigos muito elucidativos - em especial a série de artigos intitulada "How it works". Este módulo é praticamente a tradução de um dos artigos desta série, o "Disassembly of a DOS Floppy Boot Sector". Se tiver um tempinho, visite o site do Hale porque tem muita coisa interessante.