Emulating Linus Torvalds: Create your own operating system from 0 (V)

In this fifth installment we will see a table quite similar to the GDT both in theory and in use, we refer to the IDT. IDT stands for Interrupt Description Table y is a table that is used to handle interrupts that occur. For example, someone makes a division by 0, the function in charge of processing is called. These functions are the ISR (Interrupt Service Routines). So let's create the IDT and add some ISR.

First we are going to declare the structures corresponding to the 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));

As can be seen if you compare it with the GDT, the Ptr structure is identical and the Entry is quite similar. Therefore the functions of putting an entry (SetGate) and installing (Install) are very similar.

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;
}

Install:

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();

If we look we will see that the install function uses the ND :: Memory :: Set function that we had declared in the other post. We can also see how we don't make any calls to SetGate yet and we call ND :: IDT :: Flush, for this function we use the asm volatile statement again:

asm volatile("lidtl (idtptr)");

If everything goes well and we make an aesthetic arrangement it should look like this:

NextDivel-RTD

Okay, now we are going to start filling the IDT with interrupts. Here I am going to create only one but for the rest it would be the same. I'm going to do the divide by zero break. As you well know in mathematics, a number cannot be divided by 0. If this happens in the processor, an exception is generated since it cannot continue. In IDT the first interrupt in list (0) corresponds to this event.

We add this between the memory setting and the flush within the Install function of the IDT:

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

The callback function is going to be ND :: ISR :: ISR1 which is quite simple although we must use ASM:

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

We will define ND_ISR_Common as a function in C language. To save files and improve readability we can use 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"
);
}

This code in ASM can be a bit difficult to understand but this is because we are going to declare a structure in C to access the data generated by the interrupt. Obviously, if you didn't want that, you could just call the Kernel Panic in ND :: ISR :: ISR1 or something like that. The structure has a shape such that:

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;
};

And finally we make the ND_ISR_Handler function (also with a C link) in which we show a kernel panic and a small description of the error according to the one we have in a list of errors.

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

Good and with this we are already able to handle this interruption. With the rest of interruptions it would happen similar except that there are some that return parameters and we would use the reg structure to obtain it. However, you may wonder how we know if it really works. To test if it works we are going to introduce a simple line after the ND :: IDT :: Install ():

int sum=10/0;

If we compile it will give us a warning and if we try to execute it we will get a nice screen:

NextDivel-ISR


And with this ends this post, I think it is one of the most extensive but quite functional.


Leave a Comment

Your email address will not be published. Required fields are marked with *

*

*

  1. Responsible for the data: Miguel Ángel Gatón
  2. Purpose of the data: Control SPAM, comment management.
  3. Legitimation: Your consent
  4. Communication of the data: The data will not be communicated to third parties except by legal obligation.
  5. Data storage: Database hosted by Occentus Networks (EU)
  6. Rights: At any time you can limit, recover and delete your information.

  1.   mesodabler said

    I switched to LFS, it is more continuous.

  2.   eliotime3000 said

    Holy ... Anyway, the tutorials are good.

  3.   sc said

    Very good, I've been following it from the beginning. Could you attach the codes to each trailer?

    1.    AdrianArroyoStreet said

      You have all the source code available on GitHub: http://github.com/AdrianArroyoCalle/next-divel From there you can download a ZIP, a TAR.GZ or just use git.

  4.   nuanced said

    Hahaha very good! Approve! 🙂