소스 검색

Normalize indentation in linux-bootstrap-1's code sections to improve readability

Matthieu Tardy 9 년 전
부모
커밋
c0c69b8dc0
1개의 변경된 파일66개의 추가작업 그리고 66개의 파일을 삭제
  1. 66 66
      Booting/linux-bootstrap-1.md

+ 66 - 66
Booting/linux-bootstrap-1.md

@@ -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.