mirror of
https://github.com/0xAX/linux-insides.git
synced 2025-01-03 12:20:56 +00:00
Some more cleanups for GDT and protected mode
This commit is contained in:
parent
90608ac7ce
commit
c2c7f33435
@ -20,16 +20,18 @@ Protected mode
|
|||||||
|
|
||||||
Before we can move to the native Intel64 [Long Mode](http://en.wikipedia.org/wiki/Long_mode), the kernel must switch the CPU into protected mode.
|
Before we can move to the native Intel64 [Long Mode](http://en.wikipedia.org/wiki/Long_mode), the kernel must switch the CPU into protected mode.
|
||||||
|
|
||||||
What is [protected mode](https://en.wikipedia.org/wiki/Protected_mode)? Protected mode was first added to the x86 architecture in 1982 and was the main mode of Intel processors from the [80286](http://en.wikipedia.org/wiki/Intel_80286) processor until Intel 64 and long mode came. The Main reason to move away from [real mode](http://wiki.osdev.org/Real_Mode) is that there is very limited access to the RAM. As you may remember from the previous part, there is only 2^20 bytes or 1 megabyte, sometimes even only 640 kilobytes of RAM available in real mode.
|
What is [protected mode](https://en.wikipedia.org/wiki/Protected_mode)? Protected mode was first added to the x86 architecture in 1982 and was the main mode of Intel processors from the [80286](http://en.wikipedia.org/wiki/Intel_80286) processor until Intel 64 and long mode came.
|
||||||
|
|
||||||
Protected mode brought many changes, but the main one is the difference in memory management. The 20-bit address bus was replaced with a 32-bit address bus. It allows access to 4-gigabytes of physical address space vs 1MB of real mode. Also [paging](http://en.wikipedia.org/wiki/Paging) support was added, which you can read about in the next sections.
|
The Main reason to move away from [Real mode](http://wiki.osdev.org/Real_Mode) is that there is very limited access to the RAM. As you may remember from the previous part, there is only 2<sup>20</sup> bytes or 1 Megabyte, sometimes even only 640 Kilobytes of RAM available in the Real mode.
|
||||||
|
|
||||||
Memory management in protected mode is divided into two, almost independent parts:
|
Protected mode brought many changes, but the main one is the difference in memory management. The 20-bit address bus was replaced with a 32-bit address bus. It allowed access to 4 Gigabytes of memory vs 1 Megabyte of real mode. Also [paging](http://en.wikipedia.org/wiki/Paging) support was added, which you can read about in the next sections.
|
||||||
|
|
||||||
|
Memory management in Protected mode is divided into two, almost independent parts:
|
||||||
|
|
||||||
* Segmentation
|
* Segmentation
|
||||||
* Paging
|
* Paging
|
||||||
|
|
||||||
Here we will only see segmentation. Paging will be discussed in the next sections.
|
Here we will only see Segmentation. Paging will be discussed in the next sections.
|
||||||
|
|
||||||
As you can read in the previous part, addresses consist of two parts in real mode:
|
As you can read in the previous part, addresses consist of two parts in real mode:
|
||||||
|
|
||||||
@ -42,30 +44,30 @@ And we can get the physical address if we know these two parts by:
|
|||||||
PhysicalAddress = Segment * 16 + Offset
|
PhysicalAddress = Segment * 16 + Offset
|
||||||
```
|
```
|
||||||
|
|
||||||
Memory segmentation was completely redone in protected mode. There are no 64 kilobyte fixed-size segments. Instead, the size and location of each segment is described by an associated data structure called segment descriptor. The segment descriptors are stored in the `Global Descriptor Table` (GDT).
|
Memory segmentation was completely redone in protected mode. There are no 64 Kilobyte fixed-size segments. Instead, the size and location of each segment is described by an associated data structure called _Segment Descriptor_. The segment descriptors are stored in a data structure called `Global Descriptor Table` (GDT).
|
||||||
|
|
||||||
The GDT is a structure which resides in memory. There is no fixed place for it in the memory so, its address is stored in the special `GDTR` register. Later we will see the GDT loading in the linux kernel code. There will be an operation for loading it into memory, something like:
|
The GDT is a structure which resides in memory. It has no fixed place in the memory so, its address is stored in the special `GDTR` register. Later we will see the GDT loading in the Linux kernel code. There will be an operation for loading it into memory, something like:
|
||||||
|
|
||||||
```assembly
|
```assembly
|
||||||
lgdt gdt
|
lgdt gdt
|
||||||
```
|
```
|
||||||
|
|
||||||
where the `lgdt` instruction loads the base address and limit of global descriptor table to the `GDTR` register. `GDTR` is a 48-bit register and consists of two parts:
|
where the `lgdt` instruction loads the base address and limit(size) of global descriptor table to the `GDTR` register. `GDTR` is a 48-bit register and consists of two parts:
|
||||||
|
|
||||||
* size(16-bit) of global descriptor table;
|
* size(16-bit) of global descriptor table;
|
||||||
* address(32-bit) of the global descriptor table.
|
* address(32-bit) of the global descriptor table.
|
||||||
|
|
||||||
The global descriptor table contains `descriptors` which describe memory segments. Every descriptor is 64-bits long. The general scheme of a descriptor is:
|
As mentioned above the GDT contains `segment descriptors` which describe memory segments. Each descriptor is 64-bits in size. The general scheme of a descriptor is:
|
||||||
|
|
||||||
```
|
```
|
||||||
31 24 19 16 7 0
|
31 24 19 16 7 0
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
| | |B| |A| | | | |0|E|W|A| |
|
| | |B| |A| | | | |0|E|W|A| |
|
||||||
| BASE 31:24 |G|/|L|V| LIMIT |P|DPL|S| TYPE | BASE 23:16 | 4
|
| BASE 31:24 |G|/|L|V| LIMIT |P|DPL|S| TYPE | BASE 23:16 | 4
|
||||||
| | |D| |L| 19:16| | | |1|C|R|A| |
|
| | |D| |L| 19:16 | | | |1|C|R|A| |
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
| | |
|
| | |
|
||||||
| BASE 15:0 | LIMIT 15:0 | 0
|
| BASE 15:0 | LIMIT 15:0 | 0
|
||||||
| | |
|
| | |
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
```
|
```
|
||||||
@ -74,16 +76,16 @@ Don't worry, I know it looks a little scary after real mode, but it's easy. For
|
|||||||
|
|
||||||
1. Limit[20-bits] is at 0-15,16-19 bits. It defines `length_of_segment - 1`. It depends on `G`(Granularity) bit.
|
1. Limit[20-bits] is at 0-15,16-19 bits. It defines `length_of_segment - 1`. It depends on `G`(Granularity) bit.
|
||||||
|
|
||||||
* if `G` (bit 55) is 0 and segment limit is 0, the size of the segment is 1 byte
|
* if `G` (bit 55) is 0 and segment limit is 0, the size of the segment is 1 Byte
|
||||||
* if `G` is 1 and segment limit is 0, the size of the segment is 4096 bytes
|
* if `G` is 1 and segment limit is 0, the size of the segment is 4096 Bytes
|
||||||
* if `G` is 0 and segment limit is 0xfffff, the size of the segment is 1 megabyte
|
* if `G` is 0 and segment limit is 0xfffff, the size of the segment is 1 Megabyte
|
||||||
* if `G` is 1 and segment limit is 0xfffff, the size of the segment is 4 gigabytes
|
* if `G` is 1 and segment limit is 0xfffff, the size of the segment is 4 Gigabytes
|
||||||
|
|
||||||
So, it means that if
|
So, it means that if
|
||||||
* if G is 0, Limit is interpreted in terms of 1 Byte.
|
* if G is 0, Limit is interpreted in terms of 1 Byte and the maximum size of the segment can be 1 Megabyte.
|
||||||
* if G is 1, Limit is interpreted in terms of 4096 Bytes = 4 KBytes.
|
* if G is 1, Limit is interpreted in terms of 4096 Bytes = 4 KBytes = 1 Page and the maximum size of the segment can be 4 Gigabytes. Actually when G is 1, the value of Limit is shifted to the left by 12 bits. So, 20 bits + 12 bits = 32 bits and 2<sup>32</sup> = 4 Gigabytes.
|
||||||
|
|
||||||
2. Base[32-bits] is at (0-15, 32-39 and 56-63 bits). It defines the physical address of the segment's start address.
|
2. Base[32-bits] is at (0-15, 32-39 and 56-63 bits). It defines the physical address of the segment's starting location.
|
||||||
|
|
||||||
3. Type/Attribute (40-47 bits) defines the type of segment and kinds of access to it.
|
3. Type/Attribute (40-47 bits) defines the type of segment and kinds of access to it.
|
||||||
* `S` flag at bit 44 specifies descriptor type. If `S` is 0 then this segment is a system segment, whereas if `S` is 1 then this is a code or data segment (Stack segments are data segments which must be read/write segments).
|
* `S` flag at bit 44 specifies descriptor type. If `S` is 0 then this segment is a system segment, whereas if `S` is 1 then this is a code or data segment (Stack segments are data segments which must be read/write segments).
|
||||||
@ -125,13 +127,13 @@ As we can see the first bit(bit 43) is `0` for a _data_ segment and `1` for a _c
|
|||||||
|
|
||||||
4. DPL[2-bits] (Descriptor Privilege Level) is at bits 45-46. It defines the privilege level of the segment. It can be 0-3 where 0 is the most privileged.
|
4. DPL[2-bits] (Descriptor Privilege Level) is at bits 45-46. It defines the privilege level of the segment. It can be 0-3 where 0 is the most privileged.
|
||||||
|
|
||||||
5. P flag(bit 47) - indicates if the segment is present in memory or not.
|
5. P flag(bit 47) - indicates if the segment is present in memory or not. If P is 0, the segment will be presented as _invalid_ and the processor will refuse to read this segment.
|
||||||
|
|
||||||
6. AVL flag(bit 52) - Available and reserved bits.
|
6. AVL flag(bit 52) - Available and reserved bits. It is ignored in Linux.
|
||||||
|
|
||||||
7. L flag(bit 53) - indicates whether a code segment contains native 64-bit code. If 1 then the code segment executes in 64 bit mode.
|
7. L flag(bit 53) - indicates whether a code segment contains native 64-bit code. If 1 then the code segment executes in 64 bit mode.
|
||||||
|
|
||||||
8. B/D flag(bit 54) - default operation size/default stack pointer size and/or upper bound.
|
8. D/B flag(bit 54) - Default/Big flag represents the operand size i.e 16/32 bits. If it is set then 32 bit otherwise 16.
|
||||||
|
|
||||||
Segment registers don't contain the base address of the segment as in real mode. Instead they contain a special structure - `Segment Selector`. Each Segment Descriptor has an associated Segment Selector. `Segment Selector` is a 16-bit structure:
|
Segment registers don't contain the base address of the segment as in real mode. Instead they contain a special structure - `Segment Selector`. Each Segment Descriptor has an associated Segment Selector. `Segment Selector` is a 16-bit structure:
|
||||||
|
|
||||||
@ -141,7 +143,10 @@ Segment registers don't contain the base address of the segment as in real mode.
|
|||||||
-----------------------------
|
-----------------------------
|
||||||
```
|
```
|
||||||
|
|
||||||
Where `Index` shows the index number of the descriptor in the GDT. `TI` shows where to search for the descriptor: in the Global Descriptor Table(GDT) or Local Descriptor Table. And `RPL` is the privilege level.
|
Where,
|
||||||
|
* **Index** shows the index number of the descriptor in the GDT.
|
||||||
|
* **TI**(Table Indicator) shows where to search for the descriptor. If it is 0 then search in the Global Descriptor Table(GDT) otherwise it will look in Local Descriptor Table(LDT).
|
||||||
|
* And **RPL** is Requester's Privilege Level.
|
||||||
|
|
||||||
Every segment register has a visible and hidden part.
|
Every segment register has a visible and hidden part.
|
||||||
* Visible - Segment Selector is stored here
|
* Visible - Segment Selector is stored here
|
||||||
@ -150,7 +155,7 @@ Every segment register has a visible and hidden part.
|
|||||||
The following steps are needed to get the physical address in the protected mode:
|
The following steps are needed to get the physical address in the protected mode:
|
||||||
|
|
||||||
* The segment selector must be loaded in one of the segment registers
|
* The segment selector must be loaded in one of the segment registers
|
||||||
* The CPU tries to find (by GDT address + Index from selector) and load the descriptor into the hidden part of the segment register
|
* The CPU tries to find a segment descriptor by GDT address + Index from selector and load the descriptor into the *hidden* part of the segment register
|
||||||
* Base address (from segment descriptor) + offset will be the linear address of the segment which is the physical address (if paging is disabled).
|
* Base address (from segment descriptor) + offset will be the linear address of the segment which is the physical address (if paging is disabled).
|
||||||
|
|
||||||
Schematically it will look like this:
|
Schematically it will look like this:
|
||||||
|
Loading…
Reference in New Issue
Block a user