3.7 KiB
Segmentation
This is about protected mode segmentation, which coverts liner to global addresses.
GDT
Table in memory that gives properties of segment registers.
Segment registers in protected mode point to entries of that table.
GDT is used as soon as we enter protected mode, so that's why we have to deal with it, but the preferred way of managing program memory spaces is paging.
Format straight from the Linux kernel 4.2: arch/x86/include/asm/desc_defs.h in struct desc_struct:
u16 limit0;
u16 base0;
unsigned base1: 8, type: 4, s: 1, dpl: 2, p: 1;
unsigned limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8;
g: granularity of the limit. If0, 1 byte, if1, 4KiB.
Other sources:
- Intel Manual 325384-053US Volume 3, 3.4.5 Segment Descriptors
- https://en.wikipedia.org/wiki/Global_Descriptor_Table
- http://wiki.osdev.org/GDT
Null segment selector
Null descriptor
Intel manual 3.4.2 Segment Selectors says:
The first entry of the GDT is not used by the processor. A segment selector that points to this entry of the GDT (that is, a segment selector with an index of 0 and the TI flag set to 0) is used as a “null segment selector.” The processor does not generate an exception when a segment register (other than the CS or SS registers) is loaded with a null selector. It does, however, generate an exception when a segment register holding a null selector is used to access memory. A null selector can be used to initialize unused segment registers. Loading the CS or SS register with a null segment selector causes a general-protection exception (#GP) to be generated.
I think this means that it is impossible to use the first entry. So you can do whatever you want with it?
Effect on memory access
The GDT modifies every memory access of a given segment by:
- adding an offset to it
- limiting how big the segment is
If an access is made at an offset larger than allowed: TODO some exception happens, which is like an interrupt, and gets handled by a previously registered handler.
The GDT could be used to implement virtual memory by using one segment per program:
+-----------+--------+--------------------------+
| Program 1 | Unused | Program 2 |
+-----------+--------+--------------------------+
^ ^ ^ ^
| | | |
Start1 End1 Start2 End2
The problem with that is that each program must have one segment, so if we have too many programs, fragmentation will be very large.
Paging gets around this by allowing discontinuous memory ranges of fixed size for each program.
The format of the GDT is given at: http://wiki.osdev.org/Global_Descriptor_Table
Effect on permissions
Besides fixing segment sizes, the GDT also specifies permissions to the program that is running:
- ring level: limits several things that can or not be done, in particular:
- instructions: e.g. no in / out in ring 3
- register access: e.g. cannot modify control registers like the GDTR in ring 3. Otherwise user programs could just escape restrictions by changing that!
- executable, readable and writable bits: which operations can be done
GDTR
GDT register
In 32-bit, a 6 byte register that holds:
- 2 byte length of the GDT (TODO in bytes or number of entries?)
- 4 byte address of the GDT in memory
In 64 bit, makes 10 bytes, with the address having 8 bytes
GRUB seems to setup one for you: http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html
lgdt
Loads the segment description register from memory.
TODO where is it on the Linux kernel?
Candidates:
- linux/arch/x86/kernel/head_64.S
- linux/arch/x86/boot/compressed/head_64.S