quinta-feira, 17 de janeiro de 2013

Escrevendo meu próprio Sistema Operacional (ou quase...)



Uma das coisas mais frustrantes para os estudantes de engenharia/ciência da computação são as aulas de Sistemas Operacionais. Você aprende a criar threads, pipes, schedulers e tudo mais, mas não aprende aquilo que tanto sonhava: como escrever seu próprio sistema operacional.

Bom, não vou explicar aqui como criar um Windows ou Linux. Para chegar perto isso, prepare-se para alguns anos de desenvolvimento. Mas vou mostrar os passos básicos, isto é, como escrever um Hello World básico para rodar no boot da máquina, independente de Windows, Linux, Solaris, BSD ou seja lá qual for o SO que você use.

Primeiramente, precisamos escrever um programa em Assembly puro, sem dependência nenhuma de nenhum sistema operacional. Estou utilizando o compilador NASM como exemplo, que pode ser baixado gratuitamente.

(Não vou entrar no mérito de ensinar Assembly aqui, até porque não sou um especialista na linguagem. Se quiser se aprofundar mais, existem ótimos tutoriais na net.)

O seguinte código imprime um “Ola Mundo” na tela, sem utilizar nenhuma API ou qualquer outra dependência de um Sistema Operacional:

;os.asm
jmp 0x7C0:main

main:

mov ax, 0x07C0 ;a MBR eh carregada pela bios em 0x7C0
mov ds, ax
mov es, ax
mov si, olamundo
call print_string ;chama a funcao que imprime o "Ola Mundo"
mainloop:
jmp mainloop
olamundo db 'Ola Mundo :P', 0x0D, 0x0A, 0

print_string:
lodsb ;pega o proximo byte de SI
or al, al ; compara o registrador com ele mesmo.
jz .done ; Se o resultado for zero, cai fora
mov ah, 0x0E
int 0x10 ; Imprime o caracter na tela
jmp print_string
.done:
ret


Para compilá-lo:

nasm os.asm -f bin -o os.bin

Ótimo, temos o código compilado, agora precisamos copiá-lo para o setor de boot da máquina. A menos que você queira que seu computador não faça mais nada além de mostrar a mensagem “Ola Mundo”, você não vai querer copiar isso para o setor de boot do seu HD.

Algumas opções são:
-Copiar para um pendrive e, para testar, modificar o boot da BIOS para bootar no pendrive;
-Utilizar uma máquina virtual.

Para ficar mais emocionante, vamos copiar para um pendrive (depois da brincadeira, você precisará formatá-lo para poder voltar a usá-lo normalmente, então guarde seus arquivos importantes).

Se você estiver no Linux, pode copiá-lo diretamente utilizando o comando dd:

dd if=os.bin of=/dev/fd#

Sendo # o número do seu dispositivo de pendrive. (Este comando talvez não funcione, pois algumas BIOS exigem que se coloque os bytes 0x55aa no fim do setor de boot).

No Windows, podemos escrever um programinha em C++ para realizar esta cópia. Você poderá escrever um programa similar para o Linux também, caso o comando acima não funcione.

Primeiramente, precisamos saber qual é o valor do dispositivo que acessa o pendrive. Não adianta tentar apontar para “d:\” ou algo do tipo, pois este endereço aponta para a partição fat dentro do pendrive. Queremos apontar para a MBR.

Para isso, clique com o botão direito em Meu Computador, vá em Gerenciamento e então em Gerenciamento de Disco (Disk Management).
Embaixo, você deve ter alguma coisa do tipo:

Disk 0 - C:
Disk 1 - D:

Veja qual é o disco referente ao seu Pendrive (no meu caso é Disk 2). Aí você pode apontar para ela pelo caminho:

\\.\PhysicalDrive#

(sendo # o número do disco).

O código do programa para copiar o seu código em ASM para a MBR é o seguinte:



#include <cstdio>
#include <cstdlib>
#include <io.h>
#include <windows.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

int main()
{
  FILE* arqAsm = NULL;

  unsigned char buf[512]; //A MBR tem tamanho 512

  arqAsm = fopen( "os.bin", "r" );

  fseek( arqAsm, 0, 0 );

  fread( (void*)buf, 1, 510, arqAsm ); //lemos o conteúdo do arquivo para nosso buffer

  buf[510] = 0x55; //colocamos o 0x55aa no final da MBR, pois
  buf[511] = 0xaa; //algumas BIOS precisam disso


  fclose( arqAsm );
  //aqui abrimos a MBR do pendrive
  HANDLE hFile;

  hFile = CreateFile("\\\\.\\PhysicalDrive2", //Apontando para "Disk 2". Mude para o que você for usar
  GENERIC_WRITE, // abrindo para escrita
  0,
  NULL,
  OPEN_EXISTING,
  NULL,
  NULL);

  if (hFile == INVALID_HANDLE_VALUE)
  {
    printf("Problema abrindo a MBR");
    return 0;
  }

  SetFilePointer( hFile, 0, 0, FILE_BEGIN );

  DWORD escritos;
  if ( !WriteFile( hFile, buf, 512, &escritos, NULL ) )
  {
    printf("Problema ao escrever na MBR" );
  }

  CloseHandle( hFile );

  return 0;
}



Antes de executá-lo, verifique novamente se você colocou o endereço do disco correto! Se você apontou para o seu HD, não vai mais conseguir bootar o seu Windows!

Se você quiser escrever o programa em Linux, em vez da função “CreateFile” utilize “open”, e em vez de “WriteFile” utilize “write” (note que os parametros são diferentes).

Agora é só compilar e executar. Note que, em algumas versões do Windows, será necessário executá-lo como administrador.

Agora basta reiniciar a máquina, entrar na BIOS, colocar o pendrive como boot #1 e ver o seu “Hello World” rodar!

É isso pessoal, em um próximo post eu explico com mais detalhes como a MBR funciona.


Agora você já pode sobrescrever a MBR do computador da sua namorada com uma mensagem do tipo “Eu te amo, minha linda!”. Ela vai adorar.