Emulating Linus Torvalds: Create your own operating system from scratch (IV)

Welcome back to this series of posts entitled "Emulating Linus Torvalds". Today we will see the GDT. First we have to see what the GDT is. According to Wikipedia:

The Global Descriptor Table or GDT is a data structure used by Intel x86-family processors starting with the 80286 in order to define the characteristics of the various memory areas used during program execution, including the base address, the size and access privileges like executability and writability

What translated would be a Global Descriptor Table, a data structure used in Intel x86 processors since 80286 to define the characteristics of various memory areas used during program execution.

In summary, if we are using an Intel x86 processor, we must define a GDT for correct use of memory. We are not going to do much complication and we are going to define 3 entries in the table:

  • A NULL entry, required for all tables.
  • A ticket for the section date, we will use the maximum, which in 32 bits is 4 GB.
  • A ticket for the section queues, we will use the maximum, which in 32 bits is 4 GB.

As you can see, data and code will use the same space. Ok, now we are going to implement it. For this we will use two structures, the first will be in charge of containing a pointer to the real data of our GDT. And the second will be an array with the GDT entries. Let's define them first

struct Entry{
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute__((packed));
struct Ptr{
uint16_t limit;
uint32_t base;
} __attribute__((packed));

You may have noticed a curious __attribute __ ((packed)) at the end of the structures. This tells the GCC not to optimize the structures because what we want is to pass the data as is to the processor. Now we are going to make a function to install the GDT. Before we should have declared the structures, now we are going to initialize them.

struct ND::GDT::Entry gdt[3];
struct ND::GDT::Ptr gp;
void ND::GDT::Install()
{
gp.limit=(sizeof(struct ND::GDT::Entry)*3)-1;
gp.base=(uint32_t)&gdt;
}

So we get to build the pointer that goes to our 3-input table.

If you compile using 64 bits it will most likely fail here. This is because pointers on 64-bit systems are obviously 64-bit and we are using 32-bit types here. Using the -m32 option may help for now
Now we define a common function to put the data in the inputs

void ND::GDT::SetGate(int num, uint32_t base, uint32_t limit, uint8_t access,uint8_t gran)
{
gdt[num].base_low=(base & 0xFFFF);
gdt[num].base_middle=(base >> 16) & 0xFF;
gdt[num].base_high=(base >> 24) & 0xFF;
gdt[num].limit_low=(limit & 0xFFFF);
gdt[num].granularity=(limit >> 16) & 0x0F;
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access=access;
}

And we call it 3 times from the install function

ND::GDT::SetGate(0,0,0,0,0); /* NULL segmente entry */
ND::GDT::SetGate(1,0,0xFFFFFFFF,0x9A,0xCF); /* 4 GiB for Code Segment */
ND::GDT::SetGate(2,0,0xFFFFFFFF,0x92,0xCF); /* 4 GiB for Data segment */

Finally we must tell the processor that we have a GDT, so that it can load it, and in our case when loading the kernel with GRUB, overwrite the GRUB GDT. To load the GDT there is an instruction in asm called lgdt (or lgdtl depending on the syntax), we are going to use it.

asm volatile("lgdtl (gp)");
asm volatile(
"movw $0x10, %ax \n"
"movw %ax, %ds \n"
"movw %ax, %es \n"
"movw %ax, %fs \n"
"movw %ax, %gs \n"
"movw %ax, %ss \n"
"ljmp $0x08, $next \n"
"next: \n"
);

Well once we have finished this our system will already have GDT. In the next chapter we will see the IDT, a table very similar to the GDT but with interruptions. I have put some status and confirmation messages with the GDT so NextDivel now looks like this:

NextDivel-GDT


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.   saeron said

    Perhaps a 64-bit structure is more suitable for these times, it is a delay to continue using the 8086.

    1.    AdrianArroyoStreet said

      I've been looking for information on GDT in x86_64 and I think it follows the old model with a special flag. A 32-bit address is still used. Now I don't know exactly how to do it correctly. Some links:
      http://wiki.osdev.org/Entering_Long_Mode_Directly
      http://f.osdev.org/viewtopic.php?f=1&t=16275

  2.   geronimo said

    First of all, your contributions are very good, but I think the title should be
    "Emulating Richard Stallman" or at least I think so ,,,
    regards

    1.    abimaelmartell said

      Linus created the Linux kernel, Stallman created GNU which are the Unix tools and commands.

      The title is appropriate because you are creating a nucleus.

      A greeting!

  3.   Ruby said

    Thank you very much for answering all my questions and being patient, as an assembler I only know the basics and as a C almost nothing, but I really like it, now I'm a bit confused with the GDT, let's see if I understand.

    The GDT will have the global 'descriptors' that can always be accessed by any program, and these descriptors point to the sections where (the program) is going to be executed? or it is otherwise.