Emulando Linus Torvalds: Crie seu próprio sistema operacional a partir de 0 (V)

Nesta quinta parcela veremos uma tabela bastante semelhante ao GDT tanto em teoria quanto em uso, nos referimos ao IDT. IDT significa Tabela de descrição de interrupções y é uma tabela usada para controlar interrupções que ocorrem. Por exemplo, alguém faz uma divisão por 0, a função responsável pelo processamento é chamada. Essas funções são o ISR (Interromper rotinas de serviço) Então, vamos criar o IDT e adicionar algum ISR.

Primeiro vamos declarar as estruturas correspondentes ao IDT:

struct Entry{
uint16_t base_low;
uint16_t sel;
uint8_t always0;
uint8_t flags;
uint16_t base_high;
} __attribute__((packed));
struct Ptr{
uint16_t limit;
uint32_t base;
} __attribute__((packed));

Como pode ser visto se você comparar com o GDT, a estrutura Ptr é idêntica e a Entry é bastante semelhante. Portanto, as funções de colocar uma entrada (SetGate) e instalar (Instalar) são muito semelhantes.

void ND::IDT::SetGate(uint8_t num,uint32_t base,uint16_t sel, uint8_t flags)
{
idt[num].base_low=(base & 0xFFFF);
idt[num].base_high=(base >> 16) & 0xFFFF;
idt[num].sel=sel;
idt[num].always0=0;
idt[num].flags=flags;
}

Instalação:

idtptr.limit=(sizeof(struct ND::IDT::Entry)*256)-1;
idtptr.base=(uint32_t)&idt;
ND::Memory::Set(&idt,0,sizeof(struct ND::IDT::Entry)*256);
ND::IDT::Flush();

Se olharmos, veremos que a função de instalação usa a função ND :: Memory :: Set que declaramos no outro post. Também podemos apreciar como não fazemos nenhuma chamada para SetGate ainda e chamamos ND :: IDT :: Flush, para esta função usamos a instrução volátil asm novamente:

asm volatile("lidtl (idtptr)");

Se tudo correr bem e fizermos um arranjo estético, deve ficar assim:

PróximoDivel-RTD

Ok, agora vamos começar a preencher o RTD com interrupções. Aqui vou criar apenas um, mas para o resto seria o mesmo. Vou dividir por zero. Como você bem sabe em matemática, um número não pode ser dividido por 0. Se isso acontecer no processador, uma exceção é gerada, pois não pode continuar. Em IDT, a primeira interrupção na lista (0) corresponde a este evento.

Adicionamos isso entre a configuração de memória e o flush dentro da função Instalar do IDT:

ND::IDT::SetGate(0,(unsigned)ND::ISR::ISR1,0x08,0x8E);

A função de retorno de chamada será ND :: ISR :: ISR1 que é bastante simples, embora devamos usar ASM:

void ND::ISR::ISR1()
{
asm volatile(
"cli \n"
"pushl 0 \n"
"pushl 0 \n"
"jmp ND_ISR_Common \n"
);
}

ND_ISR_Common vamos defini-lo como uma função em linguagem C. Para salvar arquivos e melhorar a legibilidade, podemos usar extern «C» {}:

extern "C"
void ND_ISR_Common()
{
asm volatile(
"pusha \n"
"push %ds \n"
"push %es \n"
"push %fs \n"
"push %gs \n"
"movw $0x10, %ax \n"
"movw %ax, %ds \n"
"movw %ax, %es \n"
"movw %ax, %fs \n"
"movw %ax, %gs \n"
"movl %esp, %eax \n"
"push %eax \n"
"movl $ND_ISR_Handler, %eax \n"
"call *%eax \n"
"popl %eax \n"
"popl %ds \n"
"popl %es \n"
"popl %fs \n"
"popl %gs \n"
"popa \n"
"addl 8, %esp \n"
"iret \n"
);
}

Este código em ASM pode ser um pouco difícil de entender, mas isso ocorre porque vamos declarar uma estrutura em C para acessar os dados gerados pela interrupção. Obviamente, se você não quiser isso, pode apenas chamar o Kernel Panic em ND :: ISR :: ISR1 ou algo parecido. A estrutura tem uma forma que:

struct regs{
uint32_t ds;
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
uint32_t int_no, err_code;
uint32_t eip, cs, eflags, useresp, ss;
};

E finalmente fazemos a função ND_ISR_Handler (também com um link C) na qual mostramos um kernel panic e uma pequena descrição do erro de acordo com o que temos em uma lista de erros.

extern "C"
void ND_ISR_Handler(struct regs *r)
{
if(r->int_no < 32) { ND::Panic::Show(exception_messages[r->int_no]);
for(;;);
}
}

Bom e com isso já estamos conseguindo lidar com essa interrupção. Com o resto das interrupções, aconteceria de forma semelhante, exceto que há algumas que retornam parâmetros e usaríamos a estrutura reg para obtê-los. No entanto, você pode se perguntar como sabemos se ele realmente funciona. Para testar se funciona, vamos introduzir uma linha simples após o ND :: IDT :: Install ():

int sum=10/0;

Se o compilarmos nos dará um aviso e se tentarmos executá-lo obteremos uma bela tela:

PróximoDivel-ISR


E com isso termina esse post, acho que é um dos mais extensos mas bastante funcionais.


Deixe um comentário

Seu endereço de email não será publicado. Campos obrigatórios são marcados com *

*

*

  1. Responsável pelos dados: Miguel Ángel Gatón
  2. Finalidade dos dados: Controle de SPAM, gerenciamento de comentários.
  3. Legitimação: Seu consentimento
  4. Comunicação de dados: Os dados não serão comunicados a terceiros, exceto por obrigação legal.
  5. Armazenamento de dados: banco de dados hospedado pela Occentus Networks (UE)
  6. Direitos: A qualquer momento você pode limitar, recuperar e excluir suas informações.

  1.   mesodable dito

    Eu fui para o LFS, é mais contínuo.

  2.   eliotime3000 dito

    Santo ... Enfim, os tutoriais são bons.

  3.   sc dito

    Muito bom, estou acompanhando desde o início. Você poderia anexar os códigos a cada trailer?

    1.    AdrianArroyoStreet dito

      Você tem todo o código-fonte disponível no GitHub: http://github.com/AdrianArroyoCalle/next-divel De lá, você pode baixar um ZIP, um TAR.GZ ou apenas usar o git.

  4.   matizado dito

    Hahaha muito bom! Aprovar! 🙂