Criando um sistema operacional com ASM e C++

O assunto que não é muito difundido, ganha um novo episódio. Com uma didática direcionada ao publico leigo e mediano, veja como é possível criar seu próprio núcleo básico de sistema operacional do zero, com Assembly e C++. Um desafio realmente tangível.

[ Hits: 59.490 ]

Por: Gabriel Marinho em 11/09/2013


Estrutura de arquivos



Agora, finalmente podemos colocar a mão na massa!

Iremos precisar criar nossa estrutura de arquivos para realizar a tarefa. Seguindo a mesma ordem hierárquica do projeto ErdOS, iremos obter:

mkdir bin boot libc
$ touch start.asm main.cpp link.ld libc/io.hpp libc/io.cpp


Para a comodidade, disponibilizo o disco de boot já criado com GRUB no repositório do ErdOS. Mas, caso queira criar o seu, sem problema algum.

cd boot
$ wget
https://github.com/gabrielbiga/ErdOS/raw/master/boot/grub.img

Com nossa estrutura pronta, já iremos poder colocar o código em seus respectivos arquivos.

Arquivo "start.asm" (lançador do SO, escrito em Assembly):

[BITS 32]

global start

start:
    mov esp, _pilha_do_sistema    ; Seleciona nova area na pilha do sistema
    jmp subir_kernel        ; Pula para o bloco onde e efetuada a chamada do kernel (main.cpp)

ALIGN 4        ; Obrigatoriamente esta parte deve estar alinhada a 4 bytes

;
; O bloco multiboot e responsavel por expor informacoes importantes para o linker
;

multiboot:
    ; Macros para o GRUB
    MULTIBOOT_PAGE_ALIGN    equ 1<<0
    MULTIBOOT_MEMORY_INFO   equ 1<<1
    MULTIBOOT_AOUT_KLUDGE   equ 1<<16
    MULTIBOOT_HEADER_MAGIC  equ 0x1BADB002
    MULTIBOOT_HEADER_FLAGS  equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
    MULTIBOOT_CHECKSUM  equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)

    EXTERN code, bss, end

    ; Expondo os cabecalhos Multiboot do GRUB, a assinatura do boot
    ; Caso queira ter mais informacoes vide: http://www.gnu.org/software/grub/manual/multiboot/multiboot.html

    dd MULTIBOOT_HEADER_MAGIC
    dd MULTIBOOT_HEADER_FLAGS
    dd MULTIBOOT_CHECKSUM

    ; Expondo enderecos fisicos para o linker (link.ld)
    dd multiboot
    dd code
    dd bss
    dd end
    dd start

;
; O bloco subir_kernel e um loop infinito e tem a funcao de chamar
; a funcao main() do nosso main.cpp
;

subir_kernel:
    extern main
    call main
    jmp $

;
; A section BSS tem a funcao de alocar o tamanho de nossa pilha
;

SECTION .bss
    resb 8192               ; Reserva e apenas reserva 8KB de memoria para a pilha
_pilha_do_sistema:

Arquivo "main.cpp" (arquivo principal, escrito em C++):

#include "libc/io.hpp"

using namespace std;

int main() {
  //Limpa nossa tela
  limpar_tela();
  //Escreve uma string na tela, no segundo parametro e colocada
  //a cor desejada, onde 0x??. O primeiro ? representa a cor de fundo
  //e o segundo ? representa a cor da fonte. 0 para preto.

  escrever("Bem vindo ao ErdOS", 0x02);

  //Loop infinito para o SO nunca parar
  for(;;);
}

Arquivo "link.ld" (parâmetros de configuração do linker, arquivo de parâmetros):

OUTPUT_FORMAT("binary")

ENTRY(start)

/* Endereco fisico onde o Kernel sera alocado */
phys = 0x00100000;

/* Exposicoes ao ld */
SECTIONS
{
  .text phys : AT(phys) {
    code = .;
    *(.text)
    *(.rodata)
    . = ALIGN(4096);
  }
  .data : AT(phys + (data - code))
  {
    data = .;
    *(.data)
    . = ALIGN(4096);
  }
  .bss : AT(phys + (bss - code))
  {
    bss = .;
    *(.bss)
    . = ALIGN(4096);
  }
  end = .;
}

Arquivo "libc/io.hpp" (definição do namespace "std", header C++):

#ifndef IO_HPP
#define IO_HPP


namespace std {
  void escrever(char*, int);
  void limpar_tela(void);
}

#endif

Arquivo "libc/io.cpp" (minúsculo driver de vídeo, escrito em C++):

#include "io.hpp"

//Define o endereco do buffer
#define BUFFER 0xb8000

/*
* std::escrever
* Escreve uma string (array de caracteres) na tela.
*/
void std::escrever(char* mensagem, int cor)
{
  char* mem = (BUFFER);
  while(*mensagem != 0)
  {
    *mem = *mensagem;
    mem++;
    mensagem++;
    *mem = (char*) cor;
    mem++;
  }
}

/*
* std::limpar_tela
* Limpa a tela.
*/
void std::limpar_tela(void)
{
  char* mem = (char*)(BUFFER);
  while(*mem != 0)
  {
    *mem = 0;
    mem++;
  }
}

Página anterior     Próxima página

Páginas do artigo
   1. Introdução / Preparação
   2. Estrutura de arquivos
   3. Compilação e linkagem
Outros artigos deste autor

Configurando o X e a placa de vídeo NVidia no Debian Sarge

Tocando arquivos MP3 no SuSE Linux

Leitura recomendada

Programando em Qt

Apreendendo a utilizar o GNU Debugger (parte 2)

C - Include e Makefile

Brincando com o editor HT

Tutorial OpenGL

  
Comentários
[1] Comentário enviado por lcavalheiro em 11/09/2013 - 04:09h

Hm... Realmente interessante

[2] Comentário enviado por zendrael em 11/09/2013 - 14:15h

Gabriel, legal o artigo!

Tem como compilar pra ARM?

[3] Comentário enviado por gabrielbiga em 11/09/2013 - 14:42h

Infelizmente não zendrael. Para rodar em ARM teriam que ser feitas algumas mudanças no lançador, meios de compilação e a troca do bootloader para um GRUB que rode em ARM. Mas está ai uma outra coisa legal a se fazer com o projeto... Um port para ARM. Voluntários? :)

[4] Comentário enviado por zendrael em 11/09/2013 - 14:59h


[3] Comentário enviado por gabrielbiga em 11/09/2013 - 14:42h:

Infelizmente não zendrael. Para rodar em ARM teriam que ser feitas algumas mudanças no lançador, meios de compilação e a troca do bootloader para um GRUB que rode em ARM. Mas está ai uma outra coisa legal a se fazer com o projeto... Um port para ARM. Voluntários? :)


Opa, eu topo! Só não sei por onde começar... Hahahah!

Talvez usando o uBoot para ARM já resolva/ajude em alguma coisa!

[5] Comentário enviado por Anonymous267 em 12/09/2013 - 10:29h

Interessante e fácil de ser feito o seu tutorial vou tentar fazer aqui obrigado.

[6] Comentário enviado por dannyalisson em 12/09/2013 - 16:58h

Parabéns, excelente artigo.

[7] Comentário enviado por Buckminster em 12/09/2013 - 19:19h

Muito bom. Parabéns.

[8] Comentário enviado por lucas-lks em 16/09/2013 - 07:42h

Muito bom! Parabéns pelo artigo, muito interessante =)

[9] Comentário enviado por lucasdona em 17/09/2013 - 15:55h

Zendrael, acho que com o código para compilar, pode tentar compilar utilizando o yocto ou Ltib.
Trabalho um pouco com arm e, já consegui compilar um drive de uma tela de toque para um arm com um kernel especifico, utilizando o código fonte do kernel que incluia o driver da tala tmb (usbtouchscreen.c). Acredito que seja possível também compilar todo o kernel.

[10] Comentário enviado por c4s3 em 25/09/2013 - 12:55h

zendrael, cara muito bom o seu post. vc conheçe outros lugares onde eu possa encontrar mais info de como começar a desenvolver a minha própria distro?

[11] Comentário enviado por marceloiaverafa em 22/11/2013 - 13:36h

quando executei este comando no terminal:

$ g++ -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -fpermissive -m32 -c libc/io.cpp

ele retornou:

libc/io.cpp: Na função ‘void std::escrever(char*, int)’:
libc/io.cpp:10:20: aviso: conversão de ‘int’ para ‘char*’ inválida [-fpermissive]
libc/io.cpp:16:16: aviso: conversão de ‘char*’ para ‘char’ inválida [-fpermissive]

[12] Comentário enviado por heitor_neto em 25/02/2014 - 14:19h

Como faço para criar o meu proprio disco de boot.

[13] Comentário enviado por SirCode em 05/12/2015 - 13:30h

Pode me explicar como funciona esses códigos ?
Essa parte principalmente não entendi nada:
MULTIBOOT_PAGE_ALIGN equ 1<<0
MULTIBOOT_MEMORY_INFO equ 1<<1
MULTIBOOT_AOUT_KLUDGE equ 1<<16
MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
Essa tambem:
EXTERN code, bss, end
Essa:
dd MULTIBOOT_HEADER_MAGIC
dd MULTIBOOT_HEADER_FLAGS
dd MULTIBOOT_CHECKSUM

dd multiboot
dd code
dd bss
dd end
dd start
Não entendi a ultima linha do start.asm
E não entendi o link.ld
E como que a variavel mem do io.cpp vai escrever na tela ?

[14] Comentário enviado por SirCode em 05/12/2015 - 13:44h

Pode fazer uma parte de como detectar o keypress ?

[15] Comentário enviado por Uchiha Beyond em 07/07/2016 - 05:02h

Pelo que reparei ele funciona apenas com o modulo para video, como ele funciona sem mudulo para o processador, Memoria RAM e Armazenamento de Memoria?

[16] Comentário enviado por nelsoncole em 15/06/2017 - 18:17h

Bom tutorial !

Ai porquê que ele parou simplesmente no "Hello World"?

[17] Comentário enviado por nelsoncole em 23/06/2017 - 22:15h

Vejo que o artigo foi criado 11/09/2013. Gostaria de poder contribuir com o projecto, adicionando nele os descritores de seguimento e de interrupção, fazer funcionar pelo menos 5 das IRQs e criar um biblioteca mais próxima a Libc.
Infelizmente não estou recebendo atenção do altor inicial.

[18] Comentário enviado por Ashura em 04/07/2017 - 14:09h

Amei ;)
I Love Programming!


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts