Global Descriptor Table
Last updated
Was this helpful?
Last updated
Was this helpful?
The Global Descriptor table is used to describe the memory segments to the CPU; which, of course, is necessary to switch from 16-bit Real Mode to 32-bit protected mode.
The Global Descriptor Table has the following struture
Each entry is 8 bytes (64 bits) and contains information that describes the segment.
Typically, additional segments for kernel code and data segments will accompany these.
The image above shows the format of a descriptor table entry. The base and segment limit fields are ignored in 64-bit protected mode. Those values are used to describe the beginning and endpoint of the segment; on 64-bit OSs running in 64-bit mode, the full linear address space of the segment is used, thus allowing those values to be ignored.
This example elegantly uses C macros that are expanded and preprocessed to dynamically set access permissions.
A few things to note. First, notice that the code segment entries for user (PL0) and kernel (PL3) allow execute and read permission; they do not support write permission. A writable code segment is a major security vulnerability.
Next, you'll notice that kernel code and data segments are given a privilege level of three, while the user equivalents have zero.
The accessed bit is set for each of these descriptor entries. If the accessed bit is not set, the CPU will set it when the segment is accessed, unless it's already set to 1 (here, it is not). If the GDT descriptor is loaded read-only pages, a page fault will be triggered. The recommendation is to set 1. How does this program guard against this condition?
The use of macros provides facilities for consistent entry creation; none of the entries in this example are read-only, thus the threat is negligible.
In the presented in the MBR post, there were two segments defined:
Proper segmentation is layout is paramount to guarantee memory safety for an OS running in protected mode. As described in the , the hardware must provide facilities to control memory access because the operating system does not intervene with CPU instructions that access memory.
The assembly example provided in the used two entries. The GDT is loaded using the lgdt instruction to load the gdt_dsecriptor label. Since you're familiar with assembly, you can see that the gdt_descriptor label is simply calculating the start and end of the gdt with all entries, including the first null entry.
OSDev Wiki provides a more elegant solution that uses a minimal amount of assembly to construct and populate the GDT. It is advisable that you read the and the on OSDev wiki.