GDT
Last updated
Thanks to GRUB, your kernel is no longer in real-mode, but already in protected mode, this mode allows us to use all the possibilities of the microprocessor such as virtual memory management, paging and safe multi-tasking.
The GDT ("Global Descriptor Table") is a data structure used to define the different memory areas: the base address, the size and access privileges like execute and write. These memory areas are called "segments".
We are going to use the GDT to define different memory segments:
"code": kernel code, used to stored the executable binary code
"data": kernel data
"stack": kernel stack, used to stored the call stack during kernel execution
"ucode": user code, used to stored the executable binary code for user program
"udata": user program data
"ustack": user stack, used to stored the call stack during execution in userland
GRUB initializes a GDT but this GDT is does not correspond to our kernel. The GDT is loaded using the LGDT assembly instruction. It expects the location of a GDT description structure:
And the C structure:
Caution: the directive __attribute__ ((packed))
signal to gcc that the structure should use as little memory as possible. Without this directive, gcc include some bytes to optimize the memory alignment and the access during execution.
Now we need to define our GDT table and then load it using LGDT. The GDT table can be stored wherever we want in memory, its address should just be signaled to the process using the GDTR registry.
The GDT table is composed of segments with the following structure:
And the C structure:
We need now to define our GDT in memory and finally load it using the GDTR registry.
We are going to store our GDT at the address:
The function init_gdt_desc in x86.cc initialize a gdt segment descriptor.
And the function init_gdt initialize the GDT, some parts of the below function will be explained later and are used for multitasking.