Skip to content

Commit 413f779

Browse files
committed
TODO
Signed-off-by: Alexander Kuleshov <kuleshovmail@gmail.com>
1 parent 8656e1a commit 413f779

1 file changed

Lines changed: 40 additions & 61 deletions

File tree

Booting/linux-bootstrap-2.md

Lines changed: 40 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,53 @@
1-
Kernel booting process. Part 2.
2-
================================================================================
1+
# Kernel booting process - Part 2.
32

4-
First steps in the kernel setup
5-
--------------------------------------------------------------------------------
3+
We have already started our journey into the Linux kernel in the previous [part](./linux-bootstrap-1.md), where we have walked through the very early stages of the booting process and first assembly instructions of the Linux kernel code. Besides different mechanisms, this code was responsible to prepare environment for [C](https://en.wikipedia.org/wiki/C_(programming_language)) programming langauge. At the end of chapter we reached a symbolic milestone - the very first call of a C function. This function has classical name - `main` and defined in the [arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/v4.16/arch/x86/boot/main.c) source code file.
64

7-
We started to dive into the Linux kernel's insides in the previous [part](linux-bootstrap-1.md) and saw the initial part of the kernel setup code. We stopped at the first call to the `main` function (which is the first function written in C) from [arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/v4.16/arch/x86/boot/main.c).
5+
From here on, we will start to see assembler code more and more rare, but it is not the end 🤓 We still will meet some assembly code on our way, but it will be more rare and rare. But now it is time for more "high level" logic!
86

9-
In this part, we will continue to research the kernel setup code and go over
10-
* what `protected mode` is,
11-
* the transition into it,
12-
* the initialization of the heap and the console,
13-
* memory detection, CPU validation and keyboard initialization
14-
* and much much more.
7+
In this part, we’ll keep digging through the kernel’s setup code and cover:
158

16-
So, let's go ahead.
9+
- What [protected mode](https://en.wikipedia.org/wiki/Protected_mode) is on x86 processors
10+
- Setup of early [heap](https://en.wikipedia.org/wiki/Memory_management#HEAP) and console
11+
- Detection of avialable memory
12+
- Validation of a CPU
13+
- Initialization of a keyboard
1714

18-
Protected mode
19-
--------------------------------------------------------------------------------
15+
Time to explore these steps in detail!
2016

21-
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.
17+
## Protected mode
2218

23-
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.
19+
The Linux kernel for x86_64 operates in a special mode so-called - [long mode](http://en.wikipedia.org/wiki/Long_mode). One of the main goal of all the setup kernel code is to switch to this mode. But before we can move to this mode, the kernel must switch the CPU into [protected mode](https://en.wikipedia.org/wiki/Protected_mode).
2420

25-
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 are only 2<sup>20</sup> bytes or 1 Megabyte, sometimes even only 640 Kilobytes of RAM available in Real mode.
21+
What is [protected mode](https://en.wikipedia.org/wiki/Protected_mode)? From the previous chpater we already know that currently CPU operates in [real mode](https://en.wikipedia.org/wiki/Real_mode). For us it is mostly means - memory segmentation. As a short reminder - to access a memory location, the combination of two CPU [registers](https://en.wikipedia.org/wiki/Processor_register) is used:
2622

27-
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 the 1 Megabyte in Real mode. Also, [paging](http://en.wikipedia.org/wiki/Paging) support was added, which you can read about in the next sections.
23+
- A segment register - `cs`, `ds`, `ss` and `es` which defines segment selector.
24+
- A general purpose register which specifies offset within the segment.
2825

29-
Memory management in Protected mode is divided into two, almost independent parts:
26+
The main motivation for switching from real mode is its memory limitation. As we saw in the previous part, real mode can address only 2<sup>20</sup> bytes. This is just 1 MB of RAM. Obviosuly modern software including an operating system kernel need more. To break this constraints, the new processor mode was introduced - protected mode.
3027

31-
* Segmentation
32-
* Paging
28+
Protected mode was instroduced to the x86 architecture in 1982 and became the primary operating mode of Intel processors, starting with the [80286](http://en.wikipedia.org/wiki/Intel_80286) until the introduction of x86_64 and long mode. This mode brought many changes and improvements, but one of the most crucial was in memory managment. The 20-bit address bus was replaced with a 32-bit address bus. It allowed access to 4 Gigabytes of memory vs the 1 Megabyte in real mode.
3329

34-
Here we will only talk about segmentation. Paging will be discussed in the next sections.
30+
Memory management in protected mode is divided into two, mostly independent mechanisms:
3531

36-
As you can read in the previous part, addresses consist of two parts in Real mode:
32+
- `Segmentation`
33+
- `Paging`
3734

38-
* Base address of the segment
39-
* Offset from the segment base
35+
For now, our attention stays on segmentation. We’ll return to paging later, once we enter 64-bit long mode.
4036

41-
And we can get the physical address if we know these two parts by:
37+
### Memory segmentation in protected mode
4238

43-
```
44-
PhysicalAddress = Segment Base * 16 + Offset
45-
```
39+
In protected mode, memory segmentation was completely redesigned. Fixed 64 KB real mode segments are gone. Instead, each segment is now defined by a special data structure called a `Segment Descriptor` which specifies the properties of a segment. The segment descriptors are stored in another special structure called `Gloabal Descrptor Table` or `GDT`. Whenever a CPU needs to find an actual physical memory address, it consults GDT. The GDT itself is just a block of memory with address stored in the special CPU register called `gdtr`. This is a 48-bit register and consists of two parts:
4640

47-
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 the _Segment Descriptor_. These segment descriptors are stored in a data structure called the `Global Descriptor Table` (GDT).
41+
- The size of the global descriptor table
42+
- The address of the global descriptor table
4843

49-
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 how the GDT is loaded in the Linux kernel code. There will be an operation for loading it from memory, something like:
44+
Later, we will see exactly how the Linux kernel builds and loads its GDT. For now, it’s enough to know that the CPU provides a dedicated instruction to load the table’s address into the GDTR register:
5045

5146
```assembly
5247
lgdt gdt
5348
```
5449

55-
where the `lgdt` instruction loads the base address and limit(size) of the global descriptor table to the `GDTR` register. `GDTR` is a 48-bit register and consists of two parts:
56-
57-
* the size(16-bit) of the global descriptor table;
58-
* the address(32-bit) of the global descriptor table.
59-
60-
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:
50+
Now let's see how segment descripotrs look like. 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:
6151

6252
```
6353
63 56 51 48 45 39 32
@@ -75,31 +65,29 @@ As mentioned above, the GDT contains `segment descriptors` which describe memory
7565
------------------------------------------------------------
7666
```
7767

78-
Don't worry, I know it looks a little scary after Real mode, but it's easy. For example LIMIT 15:0 means that bits 0-15 of the segment limit are located at the beginning of the Descriptor. The rest of it is in LIMIT 19:16, which is located at bits 48-51 of the Descriptor. So, the size of Limit is 0-19 i.e 20-bits. Let's take a closer look at it:
68+
Do not worry! I know it may look a little bit intimidating at the first glance, especially in comparison to the relatively simple addressing in real mode, but we will go through it in details. We will start from the bottom, from right to left.
7969

80-
1. Limit[20-bits] is split between bits 0-15 and 48-51. It defines the `length_of_segment - 1`. It depends on the `G`(Granularity) bit.
70+
The first field is `LIMIT 15:0`. It represents the first 16 bits of the segment limit. The second part is located at the bits `51:48`. This field provides information about the size of a segment. Having 20-bit size of the limit field, it may seem that the max size of a memory segment can be 1 MB, but it is not like that. In addition, the max size of a segment depends on the 55th `G` bit:
8171

82-
* if `G` (bit 55) is 0 and the segment limit is 0, the size of the segment is 1 Byte
83-
* if `G` is 1 and the segment limit is 0, the size of the segment is 4096 Bytes
84-
* if `G` is 0 and the segment limit is 0xfffff, the size of the segment is 1 Megabyte
85-
* if `G` is 1 and the segment limit is 0xfffff, the size of the segment is 4 Gigabytes
72+
- If `G=0` - the value of the `LIMIT` field is interpreted in bytes.
73+
- if `G=1` - the value of the `LIMIT` field is interpreted in 4 KB units called pages.
8674

87-
So, what this means is
88-
* if G is 0, Limit is interpreted in terms of 1 Byte and the maximum size of the segment can be 1 Megabyte.
89-
* 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.
75+
Based on this, we can easily calculate that the max size of a segment is 4 GB.
9076

91-
2. Base[32-bits] is split between bits 16-31, 32-39 and 56-63. It defines the physical address of the segment's starting location.
77+
The next field is `BASE`. We may see that it is split on three parts. The first part occupies bits from 16 to 31, the second part occupies bits from 32 to 39, and the last third part occupies bits from 56 to 63. The main goal of this field is to store the base address of a segment.
9278

93-
3. Type/Attribute[5-bits] is represented by bits 40-44. It defines the type of segment and how it can be accessed.
94-
* The `S` flag at bit 44 specifies the 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).
79+
The remaining of the fields in a segment descriptor represent flags which control different aspects of a segment, like for example access to a segment:
9580

96-
To determine if the segment is a code or data segment, we can check its Ex(bit 43) Attribute (marked as 0 in the above diagram). If it is 0, then the segment is a Data segment, otherwise, it is a code segment.
81+
- `Type` - describes the kind of segment.
82+
- `S` - distinguishes system segments from code and data segments.
83+
- `DPL` - provides information about the priviledge level of a segment. It can be 0-3 where 0 is the most privileged level.
84+
- `P` - tells the CPU whether a segment presented in memory
9785

9886
A segment can be of one of the following types:
9987

10088
```
10189
--------------------------------------------------------------------------------------
102-
| Type Field | Descriptor Type | Description |
90+
| Type Field | Descriptor Type | Description |
10391
|-----------------------------|-----------------|------------------------------------|
10492
| Decimal | | |
10593
| 0 E W A | | |
@@ -123,16 +111,7 @@ A segment can be of one of the following types:
123111
--------------------------------------------------------------------------------------
124112
```
125113

126-
As we can see the first bit(bit 43) is `0` for a _data_ segment and `1` for a _code_ segment. The next three bits (40, 41, 42) are either `EWA`(*E*xpansion *W*ritable *A*ccessible) or CRA(*C*onforming *R*eadable *A*ccessible).
127-
* if E(bit 42) is 0, expand up, otherwise, expand down. Read more [here](http://www.sudleyplace.com/dpmione/expanddown.html).
128-
* if W(bit 41)(for Data Segments) is 1, write access is allowed, and if it is 0, the segment is read-only. Note that read access is always allowed on data segments.
129-
* A(bit 40) controls whether the segment can be accessed by the processor or not.
130-
* C(bit 42) is the conforming bit(for code selectors). If C is 1, the segment code can be executed from a lower level privilege (e.g. user) level. If C is 0, it can only be executed from the same privilege level.
131-
* R(bit 41) controls read access to code segments; when it is 1, the segment can be read from. Write access is never granted for code segments.
132-
133-
4. DPL[2-bits] (Descriptor Privilege Level) comprises the bits 45-46. It defines the privilege level of the segment. It can be 0-3 where 0 is the most privileged level.
134-
135-
5. The 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 from this segment.
114+
TODO
136115

137116
6. AVL flag(bit 52) - Available and reserved bits. It is ignored in Linux.
138117

0 commit comments

Comments
 (0)