What is Global Descriptor Table
Global Descriptor Table :
- The Global Descriptor Table is a data structure which is used by Intel x86-family processors starting with the 80286 for the purpose of defining the characteristics of the various memory areas (segments) which are used during program execution, including the size, the base address, and access privileges like write and executable.
- Intel Corporation is an American technology company and multinational corporation. It is headquartered in Silicon Valley, in Santa Clara, California. Intel Corporation is the world’s largest manufacturer of semiconductor chips. It also develops x86 series of microprocessors.
- x86 is a series of microprocessors that are found in most personal computers (PCs).
- The Intel 80286 is also known as the iAPX 286 or Intel 286. It is a 16-bit microprocessor that was launched on February 1, 1982. It was the first CPU based on 8086 which has separate data buses and non-multiplexed addresses. It was also the first CPU with memory management and wide protection abilities.
Exactly in protected mode, in the Intel Architecture, essentially the Interrupt Service Routines and the memory management are managed by tables of descriptors. Each descriptor keeps the information about a single object, For example; a task, a service routine, a portion of data or code, etc, which the CPU may need at some time. If someone tries to fill a new value into a segment register then the CPU requires carrying out safety and access control checks to see either you are literally allowed to access that specified memory area or not. If these checks are performed successfully then useful values are cached in invisible registers of the CPU. Intel represents 3 types of tables as follows.
- The Interrupt Descriptor Table (which replaces the IVT)
- The Global Descriptor Table (GDT)
- The Local Descriptor Table (LDT)
All the above tables are represented as a linear address, size to the CPU by the LIDT, LGDT, LLDT instructions sequentially. In most cases, the OS directly informs where those tables are at boot time and then clearly, the OS goes writing or reading the tables by a pointer.
GDT overview :
The Global Descriptor Table carries entries that tell the CPU about the memory segment. It is peculiar to the IA32 architecture.
Here, we will understand GDT composition step by step as follows.
By using the LGDT assembly instruction, the GDT is loaded. It looks for the location of a structure of GDT description. :
Here, the Offset is the linear address of the descriptor table, the meaning of which is that paging applies and the Size here is the size of the descriptor table which is subtracted by 1. The size of the descriptor table is subtracted by 1 because 65535 is the maximum value for size but the GDT can only be up to 65536 bytes (which means that the entries can be maximum of 8192). Also, no GDT can carry a size of 0.
The GDT contains 8-byte entries, each having a complex structure as follows.
Here, Limit 0:15 means that the field carries 0-15 bits of the limit value. The base has 32 bits value which contains the linear address from where the segment starts and a 20-bit value here indicates the maximum addressable unit (which is either in 1 byte of units or in pages). Therefore, if someone selects the page granularity such as 4 KiB and then sets the limit value to 0 × FFFFF, the segment here will length 4 GiB address space. Following is the composition of flags and access byte shown as follows.
Symbol description –
- Pr –
It means Present Bit. It should be 1 bit for all valid sectors.
- Privl –
It means Privilege. It should contain 2 bits and the ring level where 0 = highest (kernel) and 3 = lowest (user applications).
- S –
It means Descriptor type. It must be set for data segments or code and should be made empty for system segments (e.g, Task State Segment).
- Ex –
It means Executable Bit. If Ex is 1 then code in this segment can be carried out (i.e, code selector) and if Ex is 0 then it is a data selector.
- DC –
It means Direction Bit, also known as Conforming Bit. It tells the direction. If DC = 0, then the segment grows up, and if DC = 1, then the segment grows down (means the limit is smaller than the offset).
Conforming Bit for code selectors –
If 1 code in the given segment is carried out from an equal or lower privilege level. For example, code in ring 3 far-jump to conforming code in ring 2 segments. The privl bits show the highest privilege level which is allowed to execute the segment. For example, code in ring 0 cannot far jump to a conforming code segment with privl == 0 × 2, but code in ring 2 and 3 can do this. Therefore, the privilege level remains the same, i.e, a far-jump form ring 3 to a privl == 2 — segment remains in ring 3 after the jump. If 0, then the code in this segment can only be executed from the ring set in privl.
Symbol description –
- RW –
It means Readable bit or Writable bit.
- Readable bit for code selectors :
In-case read access for this segment is allowed. Write access is never allowed for code segments.
- Writable bit for data selectors :
In case write access for this segment is allowed. Read access is always allowed for data segments.
- Ac –
It means Accessed bit. Set it to 0. When the segment is accessed the CPU sets this to 1.
- Gr –
It means Granularity bit. If 0, the limit is in 1 B blocks (byte granularity), if 1, the limit is in 4 KiB blocks (page granularity).
- Sz –
It means Size bit. If 0, the selector defines 16 bit protected mode. If 1, the selector defines 32 bit protected mode. We can have both 16 bit and 32-bit selectors at once.
What is kept in GDT :
For the purpose of keeping balance, one should always store the following items in the GDT as follows.
- The Null Descriptor is not at all referenced by the processor. Some emulators (such as — Bochs) will complain about exceptions in the limit. This descriptor is also used to store a pointer to the GDT itself (to use with the LGDT instruction). The pointer is 6 bytes wide and the null descriptor is 8 bytes wide. Therefore, GDT might be the perfect place for this.
- A TSS segment descriptor (keep a place for at least one).
- A data segment descriptor (We cannot write to a code segment, Therefore, we add this with type = 0 × 92).
- A code segment descriptor (for the kernel, it must have type = 0 × 9A).
- Room for more segments if we need them (For example — LDTs, user-level, more TSS, etc.).