Skip to content

boot-4: Improve first two sections of the 4th boot part#902

Merged
0xAX merged 1 commit intomasterfrom
boot-4/improve-introduction
Feb 16, 2026
Merged

boot-4: Improve first two sections of the 4th boot part#902
0xAX merged 1 commit intomasterfrom
boot-4/improve-introduction

Conversation

@0xAX
Copy link
Owner

@0xAX 0xAX commented Feb 9, 2026

Description

This PR improves the wording in the first sections of the 4th part of the Linux kernel booting process.

@0xAX 0xAX requested a review from klaudiagrz as a code owner February 9, 2026 17:50
@github-actions
Copy link

github-actions bot commented Feb 9, 2026

E-books generated for this pull request available at: https://github.com/0xAX/linux-insides/actions/runs/21835197541

@0xAX 0xAX force-pushed the boot-4/improve-introduction branch from 8b3b3be to 65db41e Compare February 11, 2026 16:27
@github-actions
Copy link

E-books generated for this pull request available at: https://github.com/0xAX/linux-insides/actions/runs/21913484730

@0xAX 0xAX force-pushed the boot-4/improve-introduction branch from 65db41e to c673536 Compare February 13, 2026 18:22
@0xAX 0xAX force-pushed the boot-4/improve-introduction branch from c673536 to 6a37263 Compare February 14, 2026 10:46
@github-actions
Copy link

E-books generated for this pull request available at: https://github.com/0xAX/linux-insides/actions/runs/22016058397

@0xAX
Copy link
Owner Author

0xAX commented Feb 14, 2026

CI is read because of #903

@0xAX 0xAX force-pushed the boot-4/improve-introduction branch from 6a37263 to a07792d Compare February 14, 2026 10:58
@github-actions
Copy link

E-books generated for this pull request available at: https://github.com/0xAX/linux-insides/actions/runs/22016174757

- The processor now can address up to four gigabytes of memory
- The privilege levels were set for the memory access

Despite this, the kernel is still in its early setup mode. There are many different things which the early setup code should prepare before we will reach the main kernel's entry point. Right now the processor operates in protected mode. However, protected mode is not the main mode in which x86_64 processors should operate but exists only for backward compatibility. The next crucial step is to switch to the native mode for `x86_64` - [long mode](https://en.wikipedia.org/wiki/Long_mode).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Despite this, the kernel is still in its early setup mode. There are many different things which the early setup code should prepare before we will reach the main kernel's entry point. Right now the processor operates in protected mode. However, protected mode is not the main mode in which x86_64 processors should operate but exists only for backward compatibility. The next crucial step is to switch to the native mode for `x86_64` - [long mode](https://en.wikipedia.org/wiki/Long_mode).
Despite this, the kernel is still in its early setup mode. There are many different things that the early setup code should prepare before we reach the main kernel's entry point. Right now, the processor operates in protected mode. However, protected mode is not the main mode in which `x86_64` processors should operate – it exists only for backward compatibility. The next crucial step is to switch to the native mode for `x86_64` - [long mode](https://en.wikipedia.org/wiki/Long_mode).

```

When the direction flag is clear, all string operations which usually used for copying data, like for example [stos](http://x86.renejeschke.de/html/file_module_x86_id_306.html), [scas](http://x86.renejeschke.de/html/file_module_x86_id_287.html) and others, will increment the index registers `esi` or `edi`. We need to clear the direction flag because later we will use strings operations to perform various operations such as clearing space for page tables or copying data.
When the direction flag is clear, all string or copy-like operations which usually used for copying data, like for example [stos](http://x86.renejeschke.de/html/file_module_x86_id_306.html), [scas](http://x86.renejeschke.de/html/file_module_x86_id_287.html) and others, will increment the index registers `esi` or `edi`. We need to clear the direction flag because later we will use strings operations to perform various operations such as clearing space for page tables or copying data.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
When the direction flag is clear, all string or copy-like operations which usually used for copying data, like for example [stos](http://x86.renejeschke.de/html/file_module_x86_id_306.html), [scas](http://x86.renejeschke.de/html/file_module_x86_id_287.html) and others, will increment the index registers `esi` or `edi`. We need to clear the direction flag because later we will use strings operations to perform various operations such as clearing space for page tables or copying data.
When the direction flag is clear, all string or copy-like operations used for copying data, like for example [stos](http://x86.renejeschke.de/html/file_module_x86_id_306.html) or [scas](http://x86.renejeschke.de/html/file_module_x86_id_287.html), will increment the index registers `esi` or `edi`. We need to clear the direction flag because later we will use string operations for tasks such as clearing space for page tables or copying data.

When the direction flag is clear, all string operations which usually used for copying data, like for example [stos](http://x86.renejeschke.de/html/file_module_x86_id_306.html), [scas](http://x86.renejeschke.de/html/file_module_x86_id_287.html) and others, will increment the index registers `esi` or `edi`. We need to clear the direction flag because later we will use strings operations to perform various operations such as clearing space for page tables or copying data.
When the direction flag is clear, all string or copy-like operations which usually used for copying data, like for example [stos](http://x86.renejeschke.de/html/file_module_x86_id_306.html), [scas](http://x86.renejeschke.de/html/file_module_x86_id_287.html) and others, will increment the index registers `esi` or `edi`. We need to clear the direction flag because later we will use strings operations to perform various operations such as clearing space for page tables or copying data.

The next instruction is to disable interrupts - `cli`. We already have seen it in previous chapter. The interrupts are disabled "twice" because modern bootloaders can load the kernel starting from this point but not only one that we have seen in the [first chapter](./linux-bootstrap-1.md).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The next instruction is to disable interrupts - `cli`. We already have seen it in previous chapter. The interrupts are disabled "twice" because modern bootloaders can load the kernel starting from this point but not only one that we have seen in the [first chapter](./linux-bootstrap-1.md).
The next instruction is to disable interrupts - `cli`. We have already seen it in the previous chapter. The interrupts are disabled "twice" because modern bootloaders can load the kernel starting from this point, but not only one that we have seen in the [first chapter](./linux-bootstrap-1.md).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The interrupts are disabled "twice" because modern bootloaders can load the kernel starting from this point, but not only one that we have seen in the first chapter.

This sentence is a bit unclear

The `call` instruction is used to get the real address of the kernel. This trick works because after the `call` instruction is executed, the stack should have return address on its top. In the code above we setup a temporary mini stack to get the address of the kernel and execute the call to the nearest label `1`. Since the top of the stack contains the return address, we put it into the `ebp` register. Using the last instruction we subtract the difference between the address of the label `1` and `strtup_32` address from the return address that we got at the previous step:
The `call` instruction is used to get the physical address where the kernel is actually loaded. This trick works because after the `call` instruction is executed, the stack should have the return address on top. This return address will be exactly the address of the label `1`.

In the code above, kernel setups a temporary mini stack where the return address will be stored after the `call` instruction. Right after the call, we pop this address from the stack and save it in the `ebp` register. Using the last instruction, we subtract the difference between the address of the label `1` and the `startup_32` physical address using the `rva` macro and `subl` instruction and store the result in the `ebp` register.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In the code above, kernel setups a temporary mini stack where the return address will be stored after the `call` instruction. Right after the call, we pop this address from the stack and save it in the `ebp` register. Using the last instruction, we subtract the difference between the address of the label `1` and the `startup_32` physical address using the `rva` macro and `subl` instruction and store the result in the `ebp` register.
In the code above, the kernel sets up a temporary mini stack where the return address will be stored after the `call` instruction. Right after the call, we pop this address from the stack and save it in the `ebp` register. Using the last instruction, we subtract the difference between the address of the label `1` and the `startup_32` physical address using the `rva` macro and `subl` instruction, and store the result in the `ebp` register.


In the code above, kernel setups a temporary mini stack where the return address will be stored after the `call` instruction. Right after the call, we pop this address from the stack and save it in the `ebp` register. Using the last instruction, we subtract the difference between the address of the label `1` and the `startup_32` physical address using the `rva` macro and `subl` instruction and store the result in the `ebp` register.

The `rva` macro defined in the same source code file and looks like this:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The `rva` macro defined in the same source code file and looks like this:
The `rva` macro is defined in the same source code file and looks like this:

- Second task state descriptor

We already saw the loading the Global Descriptor Table in the previous [part](./linux-bootstrap-3.md#set-up-global-descriptor-table), and now we're doing almost the same here, but we set descriptors to use `CS.L = 1` and `CS.D = 0` for execution in 64 bit mode.
We already saw the loading the Global Descriptor Table in the previous [part](./linux-bootstrap-3.md#set-up-global-descriptor-table), and now we're doing almost the same here, but we set descriptors to use `CS.L = 1` and `CS.D = 0` for execution in `64` bit mode.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
We already saw the loading the Global Descriptor Table in the previous [part](./linux-bootstrap-3.md#set-up-global-descriptor-table), and now we're doing almost the same here, but we set descriptors to use `CS.L = 1` and `CS.D = 0` for execution in `64` bit mode.
We already saw loading the Global Descriptor Table in the previous [part](./linux-bootstrap-3.md#set-up-global-descriptor-table), and now we're doing almost the same, but we set descriptors to use `CS.L = 1` and `CS.D = 0` for execution in `64` bit mode.

We already saw the loading the Global Descriptor Table in the previous [part](./linux-bootstrap-3.md#set-up-global-descriptor-table), and now we're doing almost the same here, but we set descriptors to use `CS.L = 1` and `CS.D = 0` for execution in `64` bit mode.

After the new Global Descriptor Table is loaded, the kernel can setup the new stack:
After the new Global Descriptor Table is loaded, the next step is is the setup of the stack:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
After the new Global Descriptor Table is loaded, the next step is is the setup of the stack:
After the new Global Descriptor Table is loaded, the next step is to set up the stack:

```

At the previous step we loaded new Global Descriptor Table, but all the segment registers may have selectors from old table. If those selectors point to invalid entries in the new Global Descriptor Table, next memory access can cause [General Protection Fault](https://en.wikipedia.org/wiki/General_protection_fault). Setting them to the `__BOOT_DS` which is a known-good descriptor should fix this potential fault and allow us to set proper stack pointed by the `boot_stack_end`.
At the previous step we loaded new Global Descriptor Table, but all the segment registers may have selectors from the old table. If those selectors point to invalid entries in the new Global Descriptor Table, next memory access can cause [General Protection Fault](https://en.wikipedia.org/wiki/General_protection_fault). Setting them to the `__BOOT_DS` which is a known-good descriptor should fix this potential fault and allow us to set proper stack pointed by the `boot_stack_end`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
At the previous step we loaded new Global Descriptor Table, but all the segment registers may have selectors from the old table. If those selectors point to invalid entries in the new Global Descriptor Table, next memory access can cause [General Protection Fault](https://en.wikipedia.org/wiki/General_protection_fault). Setting them to the `__BOOT_DS` which is a known-good descriptor should fix this potential fault and allow us to set proper stack pointed by the `boot_stack_end`.
In the previous step, we loaded a new Global Descriptor Table; however, all the segment registers may still have selectors from the old table. If those selectors point to invalid entries in the new Global Descriptor Table, the next memory access can cause [General Protection Fault](https://en.wikipedia.org/wiki/General_protection_fault). Setting them to `__BOOT_DS`, which is a well-known descriptor, should fix this potential fault and allow us to set the proper stack pointed by `boot_stack_end`.

At the previous step we loaded new Global Descriptor Table, but all the segment registers may have selectors from old table. If those selectors point to invalid entries in the new Global Descriptor Table, next memory access can cause [General Protection Fault](https://en.wikipedia.org/wiki/General_protection_fault). Setting them to the `__BOOT_DS` which is a known-good descriptor should fix this potential fault and allow us to set proper stack pointed by the `boot_stack_end`.
At the previous step we loaded new Global Descriptor Table, but all the segment registers may have selectors from the old table. If those selectors point to invalid entries in the new Global Descriptor Table, next memory access can cause [General Protection Fault](https://en.wikipedia.org/wiki/General_protection_fault). Setting them to the `__BOOT_DS` which is a known-good descriptor should fix this potential fault and allow us to set proper stack pointed by the `boot_stack_end`.

The last action after we loaded the new Global Descriptor Table is to reload `cs` descriptor:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The last action after we loaded the new Global Descriptor Table is to reload `cs` descriptor:
The last action after we loaded the new Global Descriptor Table is to reload the `cs` descriptor:

```

Since we can not change segment registers using simple `mov` instruction, we need to apply a trick with the `lretl` instruction. This instruction fetches the two values from the top of the stack and put the first value into the `eip` register and the second value to the `cs` register. Since this moment we have proper kernel code selector and instruction pointer values.
Since we can not change segment registers using a `mov` instruction, we need to apply a trick with the `lretl` instruction. This instruction fetches two values from the top of the stack, then put the first value into the `eip` register and the second value to the `cs` register. Since this moment, we have a proper kernel code selector and instruction pointer values.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Since we can not change segment registers using a `mov` instruction, we need to apply a trick with the `lretl` instruction. This instruction fetches two values from the top of the stack, then put the first value into the `eip` register and the second value to the `cs` register. Since this moment, we have a proper kernel code selector and instruction pointer values.
Since we can not change segment registers using the `mov` instruction, we need to apply a trick with the `lretl` instruction. This instruction fetches two values from the top of the stack, then puts the first value into the `eip` register and the second value into the `cs` register. Since this moment, we have a proper kernel code selector and instruction pointer values.

Signed-off-by: Alexander Kuleshov <kuleshovmail@gmail.com>
@0xAX 0xAX force-pushed the boot-4/improve-introduction branch from a07792d to 2e9ac6a Compare February 15, 2026 10:52
@github-actions
Copy link

E-books generated for this pull request available at: https://github.com/0xAX/linux-insides/actions/runs/22034387558

@0xAX 0xAX added the boot label Feb 15, 2026
@0xAX 0xAX merged commit ba60e20 into master Feb 16, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants