1
0
mirror of https://github.com/0xAX/linux-insides.git synced 2024-12-23 07:08:11 +00:00

errors fixed in linux-bootstrap-1.md

This commit is contained in:
0xAX 2015-01-05 12:00:13 +06:00
parent 826a6c4171
commit d2fac8dc39

View File

@ -26,7 +26,7 @@ Magic power button, what's next?
Despite that this is a series of posts about linux kernel, we will not start from kernel code (at least in this paragraph). Ok, you pressed magic power button on your laptop or desktop computer and it started to work. After the mother board sends a signal to the [power supply](http://en.wikipedia.org/wiki/Power_supply), the power supply provides the computer with the proper amount of electricity. Once motherboard receives the [power good signal](http://en.wikipedia.org/wiki/Power_good_signal), it tries to run the CPU. The CPU resets all leftover data in its registers and sets up predefined values for every register. Despite that this is a series of posts about linux kernel, we will not start from kernel code (at least in this paragraph). Ok, you pressed magic power button on your laptop or desktop computer and it started to work. After the mother board sends a signal to the [power supply](http://en.wikipedia.org/wiki/Power_supply), the power supply provides the computer with the proper amount of electricity. Once motherboard receives the [power good signal](http://en.wikipedia.org/wiki/Power_good_signal), it tries to run the CPU. The CPU resets all leftover data in its registers and sets up predefined values for every register.
[80386](http://en.wikipedia.org/wiki/Intel_80386) and later CPUs defines the following predifined data in CPU registers after the computer resets: [80386](http://en.wikipedia.org/wiki/Intel_80386) and later CPUs defines the following predefined data in CPU registers after the computer resets:
``` ```
IP 0xfff0 IP 0xfff0
@ -34,7 +34,7 @@ CS selector 0xf000
CS base 0xffff0000 CS base 0xffff0000
``` ```
The processor works in [real mode](http://en.wikipedia.org/wiki/Real_mode) now and we need to make a little retreat for understanding memory segmentation in this mode. Real mode is supported in all x86 compatible processors, from [8086](http://en.wikipedia.org/wiki/Intel_8086) to modern intel 64 CPUs. The 8086 processor had a 20 bit address bus, which means that it could work with 0-2^20 bytes address space (1 megabyte). But it only had 16 bit registers, and with 16 bit registers the maximum address is 2^16 or 0xffff (64 KB). Memory segmentation was used to make use of all of the address space. All memory was divided into small, fixed-size segments of 65535 bytes, or 64 KB. Since we can not address memory behind 64 KB with 16 bit registers, another method to do it was devised. An address consists of two parts: the beginning address of the segment and the offset from the beginning of this segment. To get a physical address in memory, we need to multiply the segment part by 16 and add the offset part: The processor works in [real mode](http://en.wikipedia.org/wiki/Real_mode) now and we need to make a little retreat for understanding memory segmentation in this mode. Real mode is supported in all x86 compatible processors, from [8086](http://en.wikipedia.org/wiki/Intel_8086) to modern intel 64 CPUs. The 8086 processor had a 20 bit address bus, which means that it could work with 0-2^20 bytes address space (1 megabyte). But it only had 16 bit registers, and with 16 bit registers the maximum address is 2^16 or 0xffff (640 kilobytes). Memory segmentation was used to make use of all of the address space. All memory was divided into small, fixed-size segments of 65535 bytes, or 64 KB. Since we can not address memory behind 64 KB with 16 bit registers, another method to do it was devised. An address consists of two parts: the beginning address of the segment and the offset from the beginning of this segment. To get a physical address in memory, we need to multiply the segment part by 16 and add the offset part:
``` ```
PhysicalAddress = Segment * 16 + Offset PhysicalAddress = Segment * 16 + Offset
@ -71,7 +71,7 @@ which we can translate to the physical address::
'0xfffffff0' '0xfffffff0'
``` ```
We get `fffffff0` which is 4GB - 16 bytes. This point is the [Reset vector](http://en.wikipedia.org/wiki/Reset_vector). This is the memory location at which CPU expects to find the first instruction to execute after reset. It contains [jump](http://en.wikipedia.org/wiki/JMP_%28x86_instruction%29) instruction which usually points to the BIOS entry point. For example if we look in [coreboot](http://www.coreboot.org/) source code, we will see it: We get `0xfffffff0` which is 4GB - 16 bytes. This point is the [Reset vector](http://en.wikipedia.org/wiki/Reset_vector). This is the memory location at which CPU expects to find the first instruction to execute after reset. It contains [jump](http://en.wikipedia.org/wiki/JMP_%28x86_instruction%29) instruction which usually points to the BIOS entry point. For example if we look in [coreboot](http://www.coreboot.org/) source code, we will see it:
```assembly ```assembly
.section ".reset" .section ".reset"
@ -132,7 +132,7 @@ We will see:
In this example we can see that this code will be executed in 16 bit real mode and will start at 0x7c00 in memory. After the start it calls [0x10](http://www.ctyme.com/intr/rb-0106.htm) interrupt which just prints `!` symbol. It fills rest of 510 bytes with zeros and finish with two magic bytes 0xaa and 0x55. In this example we can see that this code will be executed in 16 bit real mode and will start at 0x7c00 in memory. After the start it calls [0x10](http://www.ctyme.com/intr/rb-0106.htm) interrupt which just prints `!` symbol. It fills rest of 510 bytes with zeros and finish with two magic bytes 0xaa and 0x55.
Real world boot loader starts at the same point, ends with `0xaa55` bytes, but reads kernel code from device, loads it to memory, parses and passes boot parameters to kernel and etc... intead of printing one symbol :) Ok, so, from this moment bios handed control to the operating system bootloader and we can go ahead. Real world boot loader starts at the same point, ends with `0xaa55` bytes, but reads kernel code from device, loads it to memory, parses and passes boot parameters to kernel and etc... instead of printing one symbol :) Ok, so, from this moment bios handed control to the operating system bootloader and we can go ahead.
**NOTE**: as you can read above CPU is in real mode. In real mode for calculating physical address in memory uses following form: **NOTE**: as you can read above CPU is in real mode. In real mode for calculating physical address in memory uses following form:
@ -176,7 +176,7 @@ At the start of execution BIOS is not in RAM, it is located in ROM.
Bootloader Bootloader
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
Now bios transfered control to the operating system bootlader and it needs to load operating system into the memory. There are a couple of bootloaders which can boot linux, like: [Grub2](http://www.gnu.org/software/grub/), [syslinux](http://www.syslinux.org/wiki/index.php/The_Syslinux_Project) and etc... Linux kernel has [Boot protocol](https://github.com/torvalds/linux/blob/master/Documentation/x86/boot.txt) which describes how to load linux kernel. Now bios transfered control to the operating system bootloader and it needs to load operating system into the memory. There are a couple of bootloaders which can boot linux, like: [Grub2](http://www.gnu.org/software/grub/), [syslinux](http://www.syslinux.org/wiki/index.php/The_Syslinux_Project) and etc... Linux kernel has [Boot protocol](https://github.com/torvalds/linux/blob/master/Documentation/x86/boot.txt) which describes how to load linux kernel.
Let us briefly consider how grub loads linux. GRUB2 execution starts from `grub-core/boot/i386/pc/boot.S`. It starts to load from device its own kernel (not to be confused with linux kernel) and executes `grub_main` after successfully loading. Let us briefly consider how grub loads linux. GRUB2 execution starts from `grub-core/boot/i386/pc/boot.S`. It starts to load from device its own kernel (not to be confused with linux kernel) and executes `grub_main` after successfully loading.
@ -225,7 +225,7 @@ X+08000 +------------------------+
``` ```
So after bootloader trasferred control to the kernel, it starts somewhere at: So after bootloader transferred control to the kernel, it starts somewhere at:
``` ```
0x1000 + X + sizeof(KernelBootSector) + 1 0x1000 + X + sizeof(KernelBootSector) + 1
@ -322,14 +322,14 @@ for my case when kernel loaded at `0x10000`.
After jump to `start_of_setup`, needs to do following things: After jump to `start_of_setup`, needs to do following things:
* Be sure that all values of all segement registers are equal * Be sure that all values of all segment registers are equal
* Setup correct stack if need * Setup correct stack if need
* Setup [bss](http://en.wikipedia.org/wiki/.bss) * Setup [bss](http://en.wikipedia.org/wiki/.bss)
* Jump to C code at [main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c) * Jump to C code at [main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c)
Let's look at implementation. Let's look at implementation.
Segement registers align Segment registers align
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
First of all it ensures that `ds` and `es` segment registers point to the same address and enables interrupts with `sti` instruction: First of all it ensures that `ds` and `es` segment registers point to the same address and enables interrupts with `sti` instruction:
@ -348,7 +348,7 @@ _start:
.byte start_of_setup-1f .byte start_of_setup-1f
``` ```
jump, which is 512 bytes offset from the [4d 5a](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L47). Also need to align `cs` from 0x10200 to 0x10000 as all other segement registers. After that we setup stack: jump, which is 512 bytes offset from the [4d 5a](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L47). Also need to align `cs` from 0x10200 to 0x10000 as all other segment registers. After that we setup stack:
```assembly ```assembly
pushw %ds pushw %ds
@ -373,8 +373,8 @@ Actually, almost all of the setup code is preparation for C language environment
Generally, it can be 3 different cases: Generally, it can be 3 different cases:
* `ss` has valid value 0x10000 (as all other segment registers beside `cs`) * `ss` has valid value 0x10000 (as all other segment registers beside `cs`)
* `ss` is invlalid and `CAN_USE_HEAP` flag is set (see below) * `ss` is invalid and `CAN_USE_HEAP` flag is set (see below)
* `ss` is invlalid and `CAN_USE_HEAP` flag is not set (see below) * `ss` is invalid and `CAN_USE_HEAP` flag is not set (see below)
Let's look at all of these cases: Let's look at all of these cases: