mirror of
https://github.com/0xAX/linux-insides.git
synced 2024-12-22 14:48:08 +00:00
Normalize indentation in linux-bootstrap-1's code sections to improve readability
This commit is contained in:
parent
7f8146db1e
commit
c0c69b8dc0
@ -70,26 +70,26 @@ The starting address is formed by adding the base address to the value in the EI
|
||||
We get `0xfffffff0` which is 4GB - 16 bytes. This point is called the [Reset vector](http://en.wikipedia.org/wiki/Reset_vector). This is the memory location at which the CPU expects to find the first instruction to execute after reset. It contains a [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 the [coreboot](http://www.coreboot.org/) source code, we see:
|
||||
|
||||
```assembly
|
||||
.section ".reset"
|
||||
.code16
|
||||
.globl reset_vector
|
||||
.section ".reset"
|
||||
.code16
|
||||
.globl reset_vector
|
||||
reset_vector:
|
||||
.byte 0xe9
|
||||
.int _start - ( . + 2 )
|
||||
...
|
||||
.byte 0xe9
|
||||
.int _start - ( . + 2 )
|
||||
...
|
||||
```
|
||||
|
||||
Here we can see the jmp instruction [opcode](http://ref.x86asm.net/coder32.html#xE9) - 0xe9 and its destination address - `_start - ( . + 2)`, and we can see that the `reset` section is 16 bytes and starts at `0xfffffff0`:
|
||||
|
||||
```
|
||||
SECTIONS {
|
||||
_ROMTOP = 0xfffffff0;
|
||||
. = _ROMTOP;
|
||||
.reset . : {
|
||||
*(.reset)
|
||||
. = 15 ;
|
||||
BYTE(0x00);
|
||||
}
|
||||
_ROMTOP = 0xfffffff0;
|
||||
. = _ROMTOP;
|
||||
.reset . : {
|
||||
*(.reset)
|
||||
. = 15 ;
|
||||
BYTE(0x00);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -191,15 +191,15 @@ Now that the BIOS has chosen a boot device and transferred control to the boot s
|
||||
As we can read in the kernel boot protocol, the bootloader must read and fill some fields of the kernel setup header, which starts at `0x01f1` offset from the kernel setup code. The kernel header [arch/x86/boot/header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S) starts from:
|
||||
|
||||
```assembly
|
||||
.globl hdr
|
||||
.globl hdr
|
||||
hdr:
|
||||
setup_sects: .byte 0
|
||||
root_flags: .word ROOT_RDONLY
|
||||
syssize: .long 0
|
||||
ram_size: .word 0
|
||||
vid_mode: .word SVGA_MODE
|
||||
root_dev: .word 0
|
||||
boot_flag: .word 0xAA55
|
||||
setup_sects: .byte 0
|
||||
root_flags: .word ROOT_RDONLY
|
||||
syssize: .long 0
|
||||
ram_size: .word 0
|
||||
vid_mode: .word SVGA_MODE
|
||||
root_dev: .word 0
|
||||
boot_flag: .word 0xAA55
|
||||
```
|
||||
|
||||
The bootloader must fill this and the rest of the headers (only marked as `write` in the Linux boot protocol, for example [this](https://github.com/torvalds/linux/blob/master/Documentation/x86/boot.txt#L354)) with values which it either got from command line or calculated. We will not see a description and explanation of all fields of the kernel setup header, we will get back to that when the kernel uses them. You can find a description of all fields in the [boot protocol](https://github.com/torvalds/linux/blob/master/Documentation/x86/boot.txt#L156).
|
||||
@ -270,8 +270,8 @@ Actually `header.S` starts from [MZ](https://en.wikipedia.org/wiki/DOS_MZ_execut
|
||||
...
|
||||
...
|
||||
pe_header:
|
||||
.ascii "PE"
|
||||
.word 0
|
||||
.ascii "PE"
|
||||
.word 0
|
||||
```
|
||||
|
||||
It needs this to load an operating system with [UEFI](https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface). We won't see how this works right now, we'll see this in one of the next chapters.
|
||||
@ -298,14 +298,14 @@ The bootloader (grub2 and others) knows about this point (`0x200` offset from `M
|
||||
So the kernel setup entry point is:
|
||||
|
||||
```assembly
|
||||
.globl _start
|
||||
.globl _start
|
||||
_start:
|
||||
.byte 0xeb
|
||||
.byte start_of_setup-1f
|
||||
.byte 0xeb
|
||||
.byte start_of_setup-1f
|
||||
1:
|
||||
//
|
||||
// rest of the header
|
||||
//
|
||||
//
|
||||
// rest of the header
|
||||
//
|
||||
```
|
||||
|
||||
Here we can see a `jmp` instruction opcode - `0xeb` to the `start_of_setup-1f` point. `Nf` notation means `2f` refers to the next local `2:` label. In our case it is label `1` which goes right after jump. It contains the rest of the setup [header](https://github.com/torvalds/linux/blob/master/Documentation/x86/boot.txt#L156). Right after the setup header we see the `.entrytext` section which starts at the `start_of_setup` label.
|
||||
@ -313,8 +313,8 @@ Here we can see a `jmp` instruction opcode - `0xeb` to the `start_of_setup-1f` p
|
||||
Actually this is the first code that runs (aside from the previous jump instruction of course). After the kernel setup got the control from the bootloader, the first `jmp` instruction is located at `0x200` (first 512 bytes) offset from the start of the kernel real mode. This we can read in the Linux kernel boot protocol and also see in the grub2 source code:
|
||||
|
||||
```C
|
||||
state.gs = state.fs = state.es = state.ds = state.ss = segment;
|
||||
state.cs = segment + 0x20;
|
||||
state.gs = state.fs = state.es = state.ds = state.ss = segment;
|
||||
state.cs = segment + 0x20;
|
||||
```
|
||||
|
||||
It means that segment registers will have the following values after kernel setup starts:
|
||||
@ -341,25 +341,25 @@ Segment registers align
|
||||
First of all it ensures that `ds` and `es` segment registers point to the same address and clears the direction flag with the `cld` instruction:
|
||||
|
||||
```assembly
|
||||
movw %ds, %ax
|
||||
movw %ax, %es
|
||||
cld
|
||||
movw %ds, %ax
|
||||
movw %ax, %es
|
||||
cld
|
||||
```
|
||||
|
||||
As I wrote earlier, grub2 loads kernel setup code at address `0x10000` and `cs` at `0x1020` because execution doesn't start from the start of file, but from:
|
||||
|
||||
```assembly
|
||||
_start:
|
||||
.byte 0xeb
|
||||
.byte start_of_setup-1f
|
||||
.byte 0xeb
|
||||
.byte start_of_setup-1f
|
||||
```
|
||||
|
||||
`jump`, which is at 512 bytes offset from the [4d 5a](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L47). It also needs to align `cs` from `0x10200` to `0x10000` as all other segment registers. After that we set up the stack:
|
||||
|
||||
```assembly
|
||||
pushw %ds
|
||||
pushw $6f
|
||||
lretw
|
||||
pushw %ds
|
||||
pushw $6f
|
||||
lretw
|
||||
```
|
||||
|
||||
push `ds` value to the stack with the address of the [6](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L494) label and execute `lretw` instruction. When we call `lretw`, it loads address of label `6` into the [instruction pointer](https://en.wikipedia.org/wiki/Program_counter) register and `cs` with the value of `ds`. After this `ds` and `cs` will have the same values.
|
||||
@ -370,10 +370,10 @@ Stack Setup
|
||||
Actually, almost all of the setup code is preparation for the C language environment in real mode. The next [step](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L467) is checking the `ss` register value and making a correct stack if `ss` is wrong:
|
||||
|
||||
```assembly
|
||||
movw %ss, %dx
|
||||
cmpw %ax, %dx
|
||||
movw %sp, %dx
|
||||
je 2f
|
||||
movw %ss, %dx
|
||||
cmpw %ax, %dx
|
||||
movw %sp, %dx
|
||||
je 2f
|
||||
```
|
||||
|
||||
This can lead to 3 different scenarios:
|
||||
@ -387,12 +387,12 @@ Let's look at all three of these scenarios:
|
||||
* `ss` has a correct address (0x10000). In this case we go to label [2](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L481):
|
||||
|
||||
```assembly
|
||||
2: andw $~3, %dx
|
||||
jnz 3f
|
||||
movw $0xfffc, %dx
|
||||
3: movw %ax, %ss
|
||||
movzwl %dx, %esp
|
||||
sti
|
||||
2: andw $~3, %dx
|
||||
jnz 3f
|
||||
movw $0xfffc, %dx
|
||||
3: movw %ax, %ss
|
||||
movzwl %dx, %esp
|
||||
sti
|
||||
```
|
||||
|
||||
Here we can see the alignment of `dx` (contains `sp` given by bootloader) to 4 bytes and a check for whether or not it is zero. If it is zero, we put `0xfffc` (4 byte aligned address before maximum segment size - 64 KB) in `dx`. If it is not zero we continue to use `sp` given by the bootloader (0xf7f4 in my case). After this we put the `ax` value to `ss` which stores the correct segment address of `0x10000` and sets up a correct `sp`. We now have a correct stack:
|
||||
@ -402,23 +402,23 @@ Here we can see the alignment of `dx` (contains `sp` given by bootloader) to 4 b
|
||||
* In the second scenario, (`ss` != `ds`). First of all put the [_end](https://github.com/torvalds/linux/blob/master/arch/x86/boot/setup.ld#L52) (address of end of setup code) value in `dx` and check the `loadflags` header field with the `testb` instruction to see whether we can use the heap or not. [loadflags](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L321) is a bitmask header which is defined as:
|
||||
|
||||
```C
|
||||
#define LOADED_HIGH (1<<0)
|
||||
#define QUIET_FLAG (1<<5)
|
||||
#define KEEP_SEGMENTS (1<<6)
|
||||
#define CAN_USE_HEAP (1<<7)
|
||||
#define LOADED_HIGH (1<<0)
|
||||
#define QUIET_FLAG (1<<5)
|
||||
#define KEEP_SEGMENTS (1<<6)
|
||||
#define CAN_USE_HEAP (1<<7)
|
||||
```
|
||||
|
||||
And as we can read in the boot protocol:
|
||||
|
||||
```
|
||||
Field name: loadflags
|
||||
Field name: loadflags
|
||||
|
||||
This field is a bitmask.
|
||||
|
||||
Bit 7 (write): CAN_USE_HEAP
|
||||
Set this bit to 1 to indicate that the value entered in the
|
||||
heap_end_ptr is valid. If this field is clear, some setup code
|
||||
functionality will be disabled.
|
||||
Set this bit to 1 to indicate that the value entered in the
|
||||
heap_end_ptr is valid. If this field is clear, some setup code
|
||||
functionality will be disabled.
|
||||
```
|
||||
|
||||
If the `CAN_USE_HEAP` bit is set, put `heap_end_ptr` in `dx` which points to `_end` and add `STACK_SIZE` (minimal stack size - 512 bytes) to it. After this if `dx` is not carry (it will not be carry, dx = _end + 512), jump to label `2` as in the previous case and make a correct stack.
|
||||
@ -435,8 +435,8 @@ BSS Setup
|
||||
The last two steps that need to happen before we can jump to the main C code, are setting up the [BSS](https://en.wikipedia.org/wiki/.bss) area and checking the "magic" signature. First, signature checking:
|
||||
|
||||
```assembly
|
||||
cmpl $0x5a5aaa55, setup_sig
|
||||
jne setup_bad
|
||||
cmpl $0x5a5aaa55, setup_sig
|
||||
jne setup_bad
|
||||
```
|
||||
|
||||
This simply compares the [setup_sig](https://github.com/torvalds/linux/blob/master/arch/x86/boot/setup.ld#L39) with the magic number `0x5a5aaa55`. If they are not equal, a fatal error is reported.
|
||||
@ -446,12 +446,12 @@ If the magic number matches, knowing we have a set of correct segment registers
|
||||
The BSS section is used to store statically allocated, uninitialized data. Linux carefully ensures this area of memory is first blanked, using the following code:
|
||||
|
||||
```assembly
|
||||
movw $__bss_start, %di
|
||||
movw $_end+3, %cx
|
||||
xorl %eax, %eax
|
||||
subw %di, %cx
|
||||
shrw $2, %cx
|
||||
rep; stosl
|
||||
movw $__bss_start, %di
|
||||
movw $_end+3, %cx
|
||||
xorl %eax, %eax
|
||||
subw %di, %cx
|
||||
shrw $2, %cx
|
||||
rep; stosl
|
||||
```
|
||||
|
||||
First of all the [__bss_start](https://github.com/torvalds/linux/blob/master/arch/x86/boot/setup.ld#L47) address is moved into `di` and the `_end + 3` address (+3 - aligns to 4 bytes) is moved into `cx`. The `eax` register is cleared (using a `xor` instruction), and the bss section size (`cx`-`di`) is calculated and put into `cx`. Then, `cx` is divided by four (the size of a 'word'), and the `stosl` instruction is repeatedly used, storing the value of `eax` (zero) into the address pointed to by `di`, automatically increasing `di` by four (this occurs until `cx` reaches zero). The net effect of this code is that zeros are written through all words in memory from `__bss_start` to `_end`:
|
||||
@ -464,7 +464,7 @@ Jump to main
|
||||
That's all, we have the stack and BSS so we can jump to the `main()` C function:
|
||||
|
||||
```assembly
|
||||
calll main
|
||||
calll main
|
||||
```
|
||||
|
||||
The `main()` function is located in [arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c). You can read about what this does in the next part.
|
||||
|
Loading…
Reference in New Issue
Block a user