OS Dev na prática para x86 – 5. BIOS

segunda-feira, 19 janeiro, 2009

OK, demorou mas aqui estou novamente.

A BIOS é um conjunto de códigos em 16 bits cuja função prover funções de acesso genérico à memória de vídeo, aos discos e outros periféricos em geral, como teclado e mouse. A natureza de 16 bits da BIOS impõe várias limitações a até onde esse hardware pode ser usado x/ (Ah, mas vovô, se a BIOS é uma coisa do $#%@, então por que não é feita em 32 bits ou 64 bits, de acordo com o processador? Ah amiguinho, o buraco é mais embaixo ¬¬).

As BIOS de hoje ainda são em 16 bits para que você possa rodar os sensacionais sistemas operacionais de 16 bits. Ainda hoje, qualquer Quad Core realiza o boot de acordo com aquele procedimento do post 2, ou seja, você ainda está em modo real (16 bits) durante o boot.

A BIOS possui inúmeras funções para facilitar a sua vida em modo real (de 16 bits). Porém, ao invés de ficar descrevendo-as, vou enviá-los para o site que eu utilizo como referência, o Ralf Brown Interrupt Jump Table. Dê uma olhada especialmente nas interrupções 0x10 (de manipulação de vídeo) e 0x13 (acesso ao disco rígido/disquete).

No boot a BIOS carrega o primeiro setor do disco (MBR no caso de HDs) de boot na memória e, se o mesmo contiver uma flag nos dois últimos bytes do setor (0x55AA), passa a executar o código presente nesse setor. Caso estes dois bytes não estejam presentes, a BIOS passa para o próximo disco de boot. Como a BIOS está rodando em modo real (16 bits), o código nesse setor também tem de ser escrito para rodar em modo real.

Mas vovô, e o modo protegido (de 32 bits)? É função do sistema operacional mudar para o modo protegido (de 32 bits) na arquitetura x86, porém para isso é necessário que o código inicial carregue mais informações do disco (vocês irão ver todas as dificuldades desse trabalho nos próximos posts), mostre a bela mensagem de “Carregando…” na tela e talvez espere alguma entrada do teclado (como o LILO ou o GRUB fazem).

No próximo artigo veremos o uso das inerrupções citadas anteriormente ao vivo no próximo artigo sobre MBR.

Mysql para CSV

segunda-feira, 1 setembro, 2008

Detesto gambiarra. Ai procurando na web encontro isso:

mysql -uexampleuser -pletmein exampledb -B -e “select * from \`person\`;” | sed ‘s/\t/”,”/g;s/^/”/;s/$/”/;s/\n//g’ > filename.csv

Olha que bonito do jeito certo *-* :

SELECT a,b,a+b INTO OUTFILE '/tmp/result.txt'
  FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
  LINES TERMINATED BY '\n'
  FROM test_table;

Sistema de arquivos no MySQL

sexta-feira, 15 agosto, 2008

Para que diabos alguém ia querer isso?

Using MySQL as a filesystem

Concordo em transformar o sistema de arquivos em banco de dados, agora o inverso acredito que não faz muito sentido rs, já que provavelmente teremos dois sistemas de arquivos atuando (o que contém os arquivos de dados do mysql e o interno ao mysql)

Estou só resmungando mesmo 😛

Password register fail

domingo, 3 agosto, 2008

(15:48:59) alex92 [n=chatzill@cable-87-116-141-142.dynamic.sbb.rs] entrou na sala.
(15:51:15) alex92: register nopassword noemail
(15:51:27) alex92 saiu da sala.
(15:51:31) encoded: fail

OS Dev na prática para x86 – 4. Geometria de Disco

sexta-feira, 17 agosto, 2007

Olá.
O nosso assunto por hora é geometria de disco.
Os tipos de discos de dados mais comuns são o disquete, o HD (também conhecido como disco rígido), o CD e o DVD. O que veremos neste post se aplica a todos esses discos mencionados.

Divisões de um disco

Para acessar os dados de um disco, o pessoal da engenharia resolveu dividir os discos em partes lógicas hierárquicas. Essas divisões são chamadas de setor, cabeça e cilindro/trilha. Olhe a figura abaixo:

Geometria de disco

Essa figura ilustra uma situação típica de um HD de vários discos. Cada lado de um disco é endereçado a partir de uma cabeça de leitura/escrita. Cada lado dos discos contém círculos concêntricos denominados trilhas (linhas vermelhas na figura). Um conjunto de trilhas de discos diferentes alinhadas entre si constitui um cilindro (linhas azuis na figura), ou seja, quando se tratar de endereçamento de dados em um único disco, cilindro e trilha serão sinônimos!
Cada trilha, por sua vez é dividida em setores (item em amarelo na figura). Os setores são as menores unidades de armazenamento de um disco (ou seja, você sempre lerá do disco e escreverá no disco no mínimo um setor) e, em geral, possuem 512 bytes em HDs e disquetes.
O acesso ao disco não corresponde exatamente ao acesso direto ao hardware do disco. Como você deve ter deduzido da imagem acima, as trilhas mais próximas do centro do disco conterão menos setores do que aquelas mais distantes, mas para o software os discos apresentam uma geometria lógica, que na maioria dos casos não é a mesma da geometria física. Você entenderá melhor daqui há algumas linhas.
Para acessar um determinado setor de um disco, existem basicamente duas maneiras (seja com a BIOS ou diretamente): CHS e LBA.
O acesso com CHS se baseia no endereço de um setor a partir de sua cabeça, cilindro e número de setor dentro da trilha (hein?). Para utilizar este modo de acesso, você deve saber quantas trilhas existem por cabeça (ou quantas trilhas existem em um lado de cada disco) e quantos setores existem em cada trilha. Os valor típico de setores por trilha eh 63, enquanto o de trilhas por cabeça você pode deduzir dividindo o número de cabeças pela quantidade de cilindros. (Mas Vo, como eu vou descobrir esses valores? Ora pessoa, pela BIOS, que é assunto do próximo artigo).
Os HDs geralmente apresentam uma geometria contendo mais de 200 cabeças (255 é o mais comum), note que para 200 cabeças, teríamos que ter no mínimo 100 discos dentro de um HD (!). Essa geometria falsa é traduzida em geometria real pela controladora do HD, então não é nada com o que você tenha que se preocupar.
O outro modo de acesso, denominado LBA permite que você acesse um setor pelo seu endereço linear, ou seja, você diz que quer ler o enésimo setor do disco, e você o lê (olha como é bem mais fácil e intuitivo). Com o LBA você acessa os setores partindo do setor 0, então um disco com aproximadamente exatos 40GB terá (40 * 1024 * 1024 * 1024 / 512 =) 83.886.080 setores e você acessará o último pedindo pelo setor 83.886.079 😀
Existem várias variações de LBA e CHS, no caso do LBA, cito apenas duas: o LBA-5, que permite o acesso à discos de até 130GB e o LBA-6, que permite acesso à discos de até uns 130 PB (não me lembro se é esse o limite real). No caso do CHS, como estou sem tempo pra explicar toda a ladainha dele, só saiba que cada variação limitava o tamanho do disco em um valor definido, como 508MB, 4GB e 8GB, por exemplo e que isso é coisa das BIOS da década passada.
Na prática, veremos os 3 modos de acesso. (Vo, você disse que iria mostrar na prática, cadê? Calma pessoa, veremos na prática, mas é importante que você saiba pelo menos o funcionamento básico da memória, um pouco de geometria de disco e de BIOS e acesso ao hardware).
Por hoje é só pessoal.

Pra você que pensou que esse blog morreria no 4º POST

sexta-feira, 15 junho, 2007

Meu HD queimou e eu não tenho backup.

Só isso =|

Criptografia em XOR

sexta-feira, 15 junho, 2007

Como a zine do motd ainda não saiu, aqui vai uma prévia de um exemplo da parte em que estou explicando IPC com signals. É um criptografador/decriptografador que usa xor e pula bytes nulos de entrada e saída (vc deve saber o porquê):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

unsigned long total = 0;

char buffer[1024];

void finaliza(int);

FILE *in, *out;

int main(int argc, char *argv[])
{

unsigned int i;
size_t len;
char *senha;

if( argc < 4 )
{

printf(“Uso: %s SENHA ARQUIVO1 ARQUIVO2\n”, argv[0]);
printf(“Criptografa ARQUIVO1 usando SENHA e salva o ”
“resultado em ARQUIVO2\n\n”);

return 0;

}

senha = argv[1];

if( strlen(senha) == 0 )
{

fprintf(stderr, “A senha deve conter pelo menos uma letra ”
“ou dígito\n”);
return -1;

}

in = fopen(argv[2], “r”);

if( ! in )
{

perror(“Erro ao abrir arquivo de origem”);

return -1;

}

out = fopen(argv[3], “w+”);

if( ! out )
{

perror(“Erro ao abrir/criar arquivo criptografado”);

fclose(in);

return -1;

}

signal(SIGINT, finaliza);
signal(SIGTERM, finaliza);

while( ! feof(in) )
{

len = fread(buffer, 1, 1024, in);

for(i = 0;i < len;i++)
{

if( buffer[i] && buffer[i] ^ senha[i % strlen(senha)] )
buffer[i] ^= senha[i % strlen(senha)];

}

fwrite(buffer, 1, len, out);

total ++;

}

fclose(in);
fclose(out);

printf(“Criptografia finalizada com sucesso (%u KB ”
“criptografados)\n”, total);

return 0;

}

void finaliza(int regs)
{

printf(“Criptografia cancelada em %u KB!\n”, total);

exit(0);

}

Como derrubar um físico

terça-feira, 8 maio, 2007

Ontem estava no Irc, e havia um vestibulando de física no canal. Quando estava saindo, lembrei que algumas pessoas ficam irritadas por eu usar utf8 aqui, então resolvi mandar um conjunto de caracteres utf8 sem significado em homenagem a essas pessoas, e olha o que aconteceu:

(00:12:42) Vovo^^: vou dormir pra amanhã estudar quimica (e python) hauhuahau
(00:12:43) rsf: quero ver acordar as 6h
(00:13:08) rsf: nossa.. soh coisa legal neh
(00:13:33) Vovo^^: com certeza =D
(00:13:36) Vovo^^: essa eh pra todos que usam iso88591: 垉斉╆𓕸򕑨䚅垅⑘񥡘塘塙夰
(00:13:47) IP_FIX: huahaehuehuhueh
(00:13:53) rsf saiu da sala (quit: Read error: 104 (Connection reset by peer)).
(00:13:57) Vovo^^: abraço pessoal o/
(00:14:15) Vovo^^: espero q ele nao tenha caido por causa dessa sequencia de caracteres ahahuahu
(00:14:25) IP_FIX: UAEHUAEHUAEHUHUAEUHAEHUAHHAEHUAEHHAEUHAEHUAEHUEAHUA
(00:14:33) rsf [n=rene@201-1-29-69.dsl.telesp.net.br] entrou na sala.
(00:14:42) rsf: eta nois eim
(00:15:17) Vovo^^: (00:13:36) Vovo^^: essa eh pra todos que usam iso88591: 垉斉╆𓕸򕑨䚅垅⑘񥡘塘塙夰
(00:15:36) rsf saiu da sala (quit: Read error: 104 (Connection reset by peer)).
(00:15:41) Vovo^^: huahuahuahua
(00:15:42) IP_FIX: UAEHUAEHUAEHUHUAEUHAEHUAHHAEHUAEHHAEHUAEHUAEHUAEHAEHHH
(00:15:48) Vovo^^: nao acredito nisso

OS Dev na prática para x86 – 3. Layout da Memória em 16 bits

sexta-feira, 4 maio, 2007

Bom, como dito anteriormente, durante o boot, estaremos trabalhando como os antigos processadores de 16 bits, e nesse caso, a memória é acessada de uma maneira específica e tem bastante peculiaridades.
Antes de iniciarmos, eu esqueci de cobrar no primeiro artigo, mas você também tem que saber hexadecimal pra usar esses posts x]
Para começar, quando estamos em 16 bits, teremos acesso a registradores de 16 bits e os mais sábios saberão que 16 bits é suficiente pra armazenar 65536 valores diferentes, e por um acaso do destino, 65536 é exatamente 64 KB, ou seja, com um registrador de 16 bits, podemos endereçar 64KB de memória (cada valor indica a posição de um byte na memória). Como 64 KB é pouquissima memória (até pra um 286), os engenheiros de hardware utilizaram uma avançada técnica onde dois registradores seriam utilizados para acessar a memória. Num mundo perfeito, 16 bits + 16 bits dariam 32 bits, e com 32 bits teríamos 4 GB de valores possíveis (e conseqüentemente poderíamos endereçar 4GB de memória), mas quem já teve um 286 sabe que as coisas não são bem assim.
O que ocorre na verdade é que a memória foi dividida em vários segmentos de 64 KB, ou seja, você utilizará um dos registradores de segmento pra indicar em qual segmento vc irá estar trabalhando. Existem vários registradores de segmento (CS, DS, ES, FS, GS e SS), e como são todos de 16 bits, você poderá utilizá-los para endereçar 65536 segmentos.
Aqui usarei a notação [x:y] para indicar segmento e offset, respectivamente. Eu ainda não tinha falado sobre offset. Como cada segmento tem 64 KB, pra obter um byte específico no seu segmento, por exemplo, o quinto byte no segmento 3, o segmento será 3 (óbvio) e o offset será 4 (lembrando que o zero é o primeiro byte). O offset é também chamado de deslocamento.

Endereçamento no segmento 2:

Byte [Seg:Off] [Seg:Off] hexadecimal
Byte 0 [2:0] [0x2:0x0]
Byte 1 [2:1] [0x2:0x1]
Byte 10 [2:10] [0x2:0x0A]
Byte 256 [2:256] [0x2:0x100]
Byte 512 [2:512] [0x2:0x200]
Byte 4096 [2:4096] [0x2:0x1000]
Byte 32768 [2:32768] [0x2:0x8000]
Byte 65535 [2:65535] [0x2:0xFFFF] (este é o último byte do segmento)

Bom, é assim que acessamos os bytes do segmento 2, mas como eu disse anteriormente, você não terá 4GB de endereços lineares mesmo tendo 64KB de segmentos e cada um com 64KB bytes (e 64KB*64KB=4GB) e isso porque cada segmento inicia em intervalos de 16 bytes (???).
Antes de entrarmos em detalhes, você precisa saber que um endereço linear corresponde a posição do byte na memória iniciando com zero, ou seja, o byte 0 está no endereço linear 0, o byte 7878 está no endereço linear 7878, o byte 1048576 está no endereço linear 1MB (pois 1048576 = 1MB), e assim em diante

Para entender melhor:

Endereço Linear Segmento:Offset
0 [0:0]
16 [0:16] e [1:0]
32 [2:0], [1:16] e [0:32]
48 [3:0], [2:16], [1:32] e [0:48]
64 [4:0], [3:16], [2:32], [1:48] e [0:64]
80 [5:0], [4:16], [3:32], [2:48], [1:64] e [0:80]
[…] […]

Então como você viu, os segmentos se sobrepõem a cada 16 bytes, ou seja, o byte zero do segmento 1 é o byte 16 do segmento zero, e assim em diante então na verdade, quando acessamos o byte 4 do segmento 2, podemos acessá-lo de três maneiras: [2:4], [1:20] e [0:36]. Estes três endereços vão te levar ao byte de endereço linear 36.
É meio estranho, mas uma vez que você entenda esse conceito, a memória será um livro aberto pra você =]
Como eu disse anteriormente, nada de 4GB de memória, pois como você agora sabe, os segmentos se sobrepõem, então até onde podemos endereçar a memória com segmentos em modo de 16 bits? Bom, para isso, podemos deduzir uma fórmula genérica que nos dê o endereço linear de um endereço composto de segmento e offset. Não é tão difícil e no fim você chegará na seguinte expressão:

endereço linear = segmento * 16 + offset

E para obter o contrário:

segmento = endereço linear / 16
offset = RESTO_DA_DIVISAO(endereço linear / 16)

Mas o que nos interessa agora é o primeiro caso. Conhecendo a fórmula, e sabendo que num registrador de 16 bits o máximo valor armazenável será 65535, poderemos obter o endereço linear do último segmento:

endereço linear = segmento *16 + offset
endereço linear = 65535 * 16 + 0
endereço linear = 1048560

Como os mais sábios notarão, estamos a 16 bytes do primeiro MB de memória. Se acessarmos o byte 16 e 17 desse segmento teremos, respectivamente, o byte 1MB e o byte (1MB + 1 byte) da memória, e se acessarmos o último byte do segmento, teremos o byte 1114095 (aprox. 1,062 MB). Então com esse modelo de segmentação da memória, podemos acessar um pouco mais de 1MB de memória. Esse “pouco mais de” trouxe problemas para placas-mãe antigas onde o bus de acesso à memória era de no 20 bits, pois com 20 bits você endereça 1MB (!). Então nesses computadores, havia a impossibilidade física de acessar mais de 1 MB de memória, mesmo com a possibilidade do acesso lógico. Para contornar este problema, surgiu um mecanismo que permitia fazer um wrap-around da memória após 1 MB, ou seja, ao acessar o endereço 1MB, esse endereço seria traduzido para o byte 0, o byte 1MB+1 seria traduzido parao byte 1, e assim em diante, então para tudo o que fosse acessado acima de 1MB, o computador realizaria a seguinte operação:

se endereço-linear > 1MB, então

endereço final = endereço linear – 1MB


Na verdade o que ocorria é um right shift de 20 bits, mas o resultado é o mesmo. Isso tudo é transparente (invisível) aos programas, tanto o sistema de segmento:offset quanto esse wrap-around a partir do endereço de 1MB.
Bom, por fim, agora que você já conhece melhor a memória, vamos ver o que há em especial nesse primeiro byte:

Endereço Descrição
[0:0x0000] Tabela de interrupções
[0:0x7C00] MBR ou Setor de boot
[0xA000:0] Memória de vídeo para modo gráfico
[0xB800:0] memória de vídeo para modo texto
[0xF000:0] BIOS

Bom, é isso. Até o próximo post sobre o disco.

OS Dev na prática para x86 – 2. Boot

domingo, 29 abril, 2007

Ou “O que acontece depois que eu aperto o botão pra ligar o PC” x]

Bom, na verdade,a maior parte não interessa mesmo, você quase não vai depender de nada do que acontecer aqui, e quando for, eu vou recordar, mas sempre tem gente querendo saber demais então vou explicar.

Em primeiro lugar, durante o boot, o mundo é de 16 bits. O processador está operando em 16 bits, a BIOS está em 16 bits e você também estará no começo do seu OS. Se você não sabe o que isso significa, vá ler um tutorial de assembly, mas pra ter uma idéia, em modo de 16 bits, você acessa por padrão apenas os 16 primeiros bits dos registradores, podendo armazenar até 65536 valores. Isso se refere às instruções também e ao acesso de memória. Felizmente você terá acesso à até aproximadamente 1MB de memória, mas isso fica para o próximo post.

O processador, através de uma avançada técnica insere no registrador IP o endereço 0xFFF0 e em CS o segmento 0xF000 (veremos segmentos no próximo post). Isso te joga no endereço 0xFFFF0 (os macacos-velhos notarão que isso é 1048560, ou seja, o processador executará a instrução no endereço 16 bytes abaixo de 1 MB) e o que tem lá??? Uma instrução jmp x]
O jmp vai mandar o fluxo de execução do processador direto para o BIOS. O BIOS geralmente é mapeado no segmento 0xF000 (não me pergunte como). O BIOS fará sua mágica (que não nos interessa como, por enquanto) e depois irá verificar se o disco de boot existe, e se é bootável mesmo. Se for, o BIOS lerá alguns bytes do disco e mandará esses dados para a memória. Por um acaso, o que existe nesses bytes, num HD, é a MBR.
Para entendermos o que é a MBR, antes temos de conhecer a memória quando o processador está em modo de 16 bits e um pouco de geometria de disco. Os próximos posts abordarão, respectivamente, estes dois assuntos e teremos um post sobre utilização do BIOS antes de entrarmos em detalhes sobre o que a MBR tem de fazer.