2 – Abra o terminal e navegue até o local onde o arquivo .AppImage foi baixado. Por exemplo, se o arquivo foi baixado para a pasta “Downloads”, você pode usar o comando “cd Downloads” para acessar essa pasta.
3 – Mova o arquivo .AppImage para o diretório /usr/local/bin com o seguinte comando:
sudo mv Todoist-1.0.9.AppImage /usr/local/bin/
4 – Conceda permissão de execução ao arquivo com o comando:
5 – Crie um atalho para o aplicativo no menu “pesquisar” do Ubuntu criando um arquivo .desktop no diretório ~/.local/share/applications. Você pode usar o editor de texto “vim” para criar o arquivo:
vim ~/.local/share/applications/todoist.desktop
6 – Adicione as seguintes informações ao arquivo .desktop:
[Desktop Entry]
Name=Todoist
Comment=Organize seu trabalho e vida, finalmente.
Exec=/usr/local/bin/Todoist-1.0.9.AppImage
Terminal=false
Type=Application
Categories=Organização;Escritório
7 – Salve o arquivo e fecha o editor de texto. O ícone do Todoist deve aparecer no menu “pesquisar” do Ubuntu na próxima vez que você abri-lo.
Antes de aprendermos a como criar um bootloader, primeiro temos que entender o que acontece com o computador ao pressionarmos o botão de Ligar.
Quando pressionamos o botão de Ligar, a energia é enviada para a CPU e a mesma trata de carregar um pequeno programa armazenado em um chip que fica localizado na placa mãe do computador.
O programa armazenado é chamado de BIOS (Basic Input/Output System), ou seja, Sistema Básico de Entrada e Saída, em placa mães mais recentes esse sistema é chamado de UEFI (Unified Extensible Firmware Interface) ou Interface de Firmware Extensível Unificada. O nosso foco será no desenvolvimento em cima da BIOS ou também chamada de LEGACY BIOS.
Quando executada, a BIOS carrega as configurações iniciais e inicializa o sistema do hardware, permitindo assim realizar as mais diversas operações, como por exemplo: acessar o disco, imprimir na tela, acessar o teclado e etc.
Também é feito uma verificação se todos os componentes do computador estão funcionando corretamente, esse procedimento é chamado de POST (Power on Self Test) Autoteste ao ligar.
Uma vez inicializada corretamente, a BIOS será responsável por encontrar o Bootloader (Carregador de inicialização) e executá-lo. Discutiremos a respeito mais a frente.
Ambiente e Ferramentas
Neste artigo, estaremos utilizando uma máquina baseada em Linux, essa escolha se deu pelo fato de que a maioria das ferramentas que utilizaremos já estão nativas nesse tipo de sistema operacional. As ferramentas utilizadas foram:
Essenciais:
NASM version 2.14.02 [1]
ld 2.3 [2]
QEMU emulator version 4.0.0 [3]
gcc 9.2.1
Opcionais:
hexdump [4]
vim
gdb 8.3
Caso esteja em um sistema operacional baseado em linux que não tem essas ferramentas basta executar o código abaixo para instalar:
$ sudo apt-get install nasm gcc qemu
Meu primeiro bootloader
O bootloader é um pequeno programa de inicialização do sistema que deve estar situado nos primeiros 512 bytes do disco, sendo que no seu final deverá estar escrito o Magic Number0xAA55, que é uma ‘assinatura’ pelo qual a BIOS reconhece que se trata de um dispositivo válido ou não [5]. Basicamente o bootloader tem como objetivo principal fazer 3 coisas:
Carregar o kernel para a memória
Passar para o modo protegido
Passar o controle para o kernel
Ele é o responsável por direcionar o fluxo de execução para o kernel do sistema operacional, agindo assim como um intermediador. A Figura 1 demonstra o processo simplificado.
Figura 1 – Processo de Boot Simplificado
Layout da memória física
O espaço de endereçamento do computador é dividido em 2 principais regiões.
A primeira região está abaixo de 1MB, conhecida como lower memory,na qual corresponde todo o endereçamento disponível possível para o chamado modo real, tal modo trabalha no máximo com 20 bits de endereçamento.
Já a segunda região é situada acima de 1MB, tambémconhecida como high memory que só poderá ser acessada no modo protegido, com endereçamentos 32/64 bits.
A BIOS éresponsável por carregar o bootloader que terá como destino oendereçode memória0x7c00 que corresponde a área chamada Boot Sector Area, percebe-se que esta área vai de 0x7c00 até 0x7E00, correspondendo a 512 bytes, como pode ser visto na Figura 2.
Figura 2 – Organização da memória de um computador
Criando o arquivo
Agora que sabemos como funciona, podemos prosseguir. Estarei utilizando o editor de texto vim do linux, mas pode ser empregado qualquer outro editor da sua escolha.
Crie um arquivo de texto em branco chamado bootloader.asm e salve. No terminal iremos compilar esse arquivo com o programa nasm.
$ nasm bootloader.asm -o bootloader.bin
Nomearemos esse trecho acima como Comando 1. Desse jeito gerando um arquivo de saída bootloader.bin, agora iremos executar o qemu com o seguinte comando:
$ qemu-system-x86_64 bootloader.bin
Nomearemos esse trecho acima como Comando 2. O resultado pode ser visto na Figura 3.
Figura 3 – Resultado do qemu
Um dos erros ocorrido foi o Boot failed: could not read the boot disk quando tentou fazer o boot pelo Hard Disk no sistema, isso se deu pelo fato de que o arquivo está vazio e a BIOS não encontrou o Magic Number.
Para resolver isso iremos abrir novamente o arquivo bootloader.asm e digitar:
times 512 db 0
Depois de escrito, salve o arquivo e execute o Comando 1e2 respectivamente. O resultado pode ser observado na Figura 4.
Figura 4 – Resultado do qemu
Agora a mensagem de erro no Hard Disk mudou para Boot failed: not a bootable disk.
Isso significa que foi encontrado um dispositivo, mas não um que seja válido, pois lembrando, necessita que no final tenha a assinatura 0xAA55para ser reconhecido como um dispositivo válido.
Entendo o binário
Para ter um outro ponto de vista do arquivo binário, iremos utilizar o seguinte comando:
$ hexdump -Cv bootloader.bin
Nomearemos esse trecho acima como Comando3. A saída do programa você poderá ver na Figura 5.
Figura 5 – Resultado do comando hexdump
Está preenchido com apenas 0 pois no arquivo só existe uma repetição para incluir o 0 em 512 bytes. Agora Iremos abrir de novo nosso arquivo bootloader.asm e acrescentar:
times 510 db 0
dw 0XAA55
Observe que de 512 foi para 510 pois o comando 0XAA55 corresponde a 2 bytes, sendo 1 byte0xAA, e outro para 0x55, 510 bytes + 2 bytes totalizando os 512 bytes necessários para o Boot Sector Area. Execute o Comando 1 e 2 respectivamente e a saída deverá ser como na Figura 6.
Figura 6 – Boot realizado
Se a mensagem que apareceu no qemu foi Booting from Hard Disk, isso indica que foi realizado o boot corretamente, ou seja, agora temos um dispositivo válido. Se executarmos o Comando 3 a saída deverá ser igual a da Figura 7.
Figura 7 – Resultado do Comando 3
Agora a BIOS já consegue encontrar o Magic Number, pois observa-se que o 0xAA55 está no final do Figura 7, na área onde fica os hexadecimais, em forma de 55 aa, está ao contrário por causa de alguns aspectos peculiares da arquitetura, que pode ser little endian ou big endian.
Escrevendo na Tela
A próxima etapa a ser feita é escrever um texto na tela, ou melhor, escrever
caracteres na tela. Para que isso ocorra você primeiro deverá conhecer o conceito de interrupções[6].
Como a leitura e escrita é fundamental para o funcionamento de um sistema, as interrupções vieram para nos auxiliar nisso, fazendo a ponte entre o hardware e o software.
Como demonstra a Figura 2 e com mais detalhes a Figura 8, a memória tem uma região que se chama IVT (Interrupt Vector Table) ou tabela de vetores de interrupção, que armazena os endereços onde estão as rotinas de tratamento de cada interrupção chamadas de ISR (Interrupt Service Routine) ou Rotina dos serviços de interrupção.
Figura 8 – Visualização do IVT na memória
Figura 9 – Fluxo de Execução de uma Interrupção
Ao se chamar a interrupção int 0x10, ocorre o seguinte:
A interrupção 0x10 é a décima interrupção, então este número é multiplicado por 4, pois cada elemento do vetor possui 4 bytes.
O endereço 0x40 é acessado na memória do IVT, e o conteúdo existente neste endereço será um novo endereço onde se encontra o código dessa interrupção no ISR.
Pula-se para esse novo endereço.
A rotina de tratamento é executada.
O controle é retornado para o programa.
## TODO – Registradores
## TODO – ASCII TABLE
Todo esse fluxo pode ser visto na Figura 9. Iremos implementar uma chamada de sistema no nosso arquivo, então abra o bootloader.asm e digite:
mov ah, 0X0E
mov al, 4Fh
int 10h
times 510-($-$$) db 0
dw 0xAA55
Salve o arquivo e execute o Comando 1e 2. Deverá aparecer na tela a letra “O” como na Figura 10.
Pense nos código mov ah, 0X0E e mov al, 4Fh como parâmetros para função de interrupção int 10h, função está responsável pelos procedimentos relacionados ao vídeo, como a escrita de caracteres na tela ou até mesmo alterar o modo de vídeo.
O valor 0x0E tem que ir para o registrador ah e significa que queremos entrar em modo texto, já 4Fh é o valor da letra “O” em hexadecimal da tabela ASCII e tem que ir no registrador al. Essas 3 primeiras linhas poderá ser feito uma analogia em C assim:
void int10(char tipo, char character);
Onde o resultado final é um caractere escrito na tela, ou também similar a função putchar()já existentedo C.
Já a penúltima linha teve que ser modificada, pois como foi inserido mais bytes, se continuasse a usar 510 bytes provavelmente iria ultrapassar a região do Boot Sector Area, para resolver esse problemas adicionamos -($-$$) onde o $ significa o endereço de memória atual e o $$ o endereço do início da seção atual, que no caso é o endereço inicial do arquivo.
Por exemplo, se as 3 primeiras linhas ocupam 5 bytes e o programa chegar no comando -($-$$) esse comando irá retornar 5 bytes, logo, 510 – 5 = 505 bytes para preencher com 0 + 2 bytes do Magic Number completando assim os 512 bytes.
Figura 10 – Saída do programa depois da chamada da Interrupção
Se quiser escrever mais caracteres é só repetir o mov al, 4Fh e int 10h no código, trocando o conteúdo de al pela hexadecimal do caractere correspondente na tabela ASCII, e depois sempre chamando a interrupção, como por exemplo: