972
edits
[unchecked revision] | [unchecked revision] |
Revolution (talk | contribs) |
m (Bot: Replace deprecated source tag with syntaxhighlight) |
||
(11 intermediate revisions by 5 users not shown) | |||
Line 1:
{{Rating|1}}
On the [[IA32_Architecture_Family|IA-32]] and [[X86-64|x86-64]] architectures, and more precisely in '''[[Protected Mode]]''' or '''[[Long Mode]]''', [[Interrupt Service Routines]] and a good deal of [[memory management]] are controlled through tables of descriptors. Each descriptor stores information about a single object (e.g. a service routine, a task, a chunk of code or data) the CPU might need at some time. If you try, for instance, to load a new value into a '''[[Segmentation|Segment Register]]''', the CPU needs to perform safety and access control checks to see whether you're actually entitled to access that specific memory area. Once the checks are performed, useful values (such as the lowest and highest addresses) are cached in invisible CPU registers.
On these architectures, there are three of this type of table: The '''[[Global Descriptor Table]]''', the '''[[Local Descriptor Table]]''' and the '''[[Interrupt Descriptor Table]]''' (which supplants the '''[[Interrupt Vector Table]]'''). Each table is defined using their size and '''[[linear address]]''' to the CPU through the '''LGDT''', '''LLDT''', and '''LIDT''' instructions respectively. In almost all use cases, these tables are only placed into memory once, at boot time, and then edited later when needed.
== Survival Glossary ==
Line 15:
: An entry in a descriptor table. These are a binary data structure that tells the CPU the attributes of a given segment.
== What
=== Basics ===
Line 35:
{|class="wikitable" style="display: inline-table;"
|+ 32-bit
!
|-
| 0x0000 || Null Descriptor || <tt>Base = 0<br>Limit = 0x00000000<br>Access Byte = 0x00<br>Flags = 0x0</tt>
Line 47:
| 0x0020 || User Mode Data Segment || <tt>Base = 0<br>Limit = 0xFFFFF<br>Access Byte = 0xF2<br>Flags = 0xC</tt>
|-
| 0x0028 || Task State Segment || <tt>Base = &TSS<br>Limit = sizeof(TSS)-1<br>Access Byte = 0x89<br>Flags = 0x0</tt>
|}
{|class="wikitable" style="display: inline-table;"
|+ 64-bit
!
|-
| 0x0000 || Null Descriptor || <tt>Base = 0<br>Limit = 0x00000000<br>Access Byte = 0x00<br>Flags = 0x0</tt>
Line 64:
| 0x0020 || User Mode Data Segment || <tt>Base = 0<br>Limit = 0xFFFFF<br>Access Byte = 0xF2<br>Flags = 0xC</tt>
|-
| 0x0028 || Task State Segment<br>('''[[Global Descriptor Table#
|}
Line 75:
{|class="wikitable" style="display: inline-table;"
|+ Small Kernel
!
|-
| 0x0000 || Null Descriptor || <tt>Base = 0<br>Limit = 0x00000000<br>Access Byte = 0x00<br>Flags = 0x0</tt>
Line 83:
| 0x0010 || Kernel Mode Data Segment || <tt>Base = 0x00800000<br>Limit = 0x003FFFFF<br>Access Byte = 0x92<br>Flags = 0xC</tt>
|-
| 0x0018 || Task State Segment || <tt>Base = &TSS<br>Limit = sizeof(TSS)-1<br>Access Byte = 0x89<br>Flags = 0x0</tt>
|}
Line 98:
{|class="wikitable"
|+ GDT
!
|-
| Preceding Entries || Null Descriptor<br>Kernel Segments<br>etc.
Line 123:
=== Filling the Table ===
The above structure of the '''GDT''' doesn't show you how to write entries in the correct format. The actual structure of descriptors is a little messy for backwards compatibility with the 286's '''GDT'''. Base address are split across three different fields and you cannot encode any limit you want
<
void encodeGdtEntry(uint8_t *target, struct GDT source)
{
Line 150 ⟶ 146:
// Encode the flags
target[6] |= (source.flags << 4);
}
</syntaxhighlight>
In order to fill your table, you will want to use this function once for each entry, with <tt>*target</tt> pointing to the logical address of the '''Segment Descriptor''' and <tt>source</tt> being a struct of your design containing the necessary information.
Line 160 ⟶ 156:
=== Telling the CPU Where the Table Is ===
Some assembly
====
The linear address should here be computed as <tt>segment * 16 + offset</tt>. <tt>GDT</tt> and <tt>GDT_end</tt> are assumed to be symbols in the current data segment.
<
gdtr DW 0 ; For limit storage
DD 0 ; For base storage
Line 181 ⟶ 177:
LGDT [gdtr]
RET
</syntaxhighlight>
====
"Flat" meaning the base of your
<
gdtr DW 0 ; For limit storage
DD 0 ; For base storage
setGdt:
MOV
MOV [gdtr], AX
MOV EAX, [ESP + 8]
MOV [gdtr + 2], EAX
</syntaxhighlight>
==== Protected Mode, Non-Flat Model ====
If your data segment has a non-zero base, you'll have to adjust the instructions of the sequence above to include the ability to add the base offset of your data segment, which should be a known value to you. You can pass it in as an argument and call this function as <tt>setGdt(limit, base, offset)</tt>.
gdtr DW 0 ; For limit storage
DD 0 ; For base storage
setGdt:
MOV AX, [esp + 4]
MOV [gdtr], AX
MOV EAX, [ESP + 8]
ADD EAX, [ESP + 12]
MOV [gdtr + 2], EAX
LGDT [gdtr]
RET
</syntaxhighlight>
====
In '''[[Long Mode]]''', the length of the '''Base''' field is 8 bytes, rather than 4. As well, the '''[[System V ABI]]''' passes the first two arguments via the '''RDI''' and '''RSI''' registers. Thus, this example code can be called as <tt>setGdt(limit, base)</tt>. As well, only a flat model is possible in long mode, so no considerations have to be made otherwise.
<syntaxhighlight lang="asm">
gdtr DW 0 ; For limit storage
DQ 0 ; For base storage
setGdt:
MOV [gdtr], DI
MOV [gdtr+2], RSI
LGDT [gdtr]
RET
</syntaxhighlight>
=== Reload Segment Registers ===
Whatever you do with the '''GDT''' has no effect on the CPU until you load
==== Protected Mode ====
In this case, reloading '''CS''' is as simple as performing a far jump to the required segment, directly after the jump instruction:
▲<source lang="asm">
<syntaxhighlight lang="asm">
reloadSegments:
; Reload CS register containing code selector:
Line 224 ⟶ 250:
MOV SS, AX
RET
</syntaxhighlight>
An explanation of the above code can be found [http://stackoverflow.com/questions/23978486/far-jump-in-gdt-in-bootloader here].
Line 232 ⟶ 258:
In '''[[Long Mode]]''' the process of changing '''CS''' is not simple as far jumps cannot be used. Using a far return is recommended instead:
<
reloadSegments:
; Reload CS register:
Line 248 ⟶ 274:
MOV SS, AX
RET
</syntaxhighlight>
== The LDT ==
Line 276 ⟶ 302:
Tool for easily creating GDT entries.
<
// Used for creating GDT segment descriptors in 64-bit integer form.
Line 358 ⟶ 384:
}
</syntaxhighlight>
== See Also ==
Line 372 ⟶ 398:
[[Category:Tutorials]]
[[Category:X86 CPU]]
[[Category:Memory Segmentation]]
|