Linux Boot Process(compressed)

Processの流れは、MBR -> GRUB kernel -> Linux kernelとなる。
MBR/GRUB kernelはLinux Kernel Codeから分離してGNU GRUBで管理されている。[GRUB](ftp://ftp.gnu.org/gnu/grub/)

GRUB kernelの関数callstack
__start -> grub_main() -> ... -> grub_cmd_linux() -> grub_cmd_initrd() -> grub_linux_boot()

```c:grub-2.00/grub-core/loader/i386/pc/linux.c
static grub_err_t
grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
		int argc, char *argv[])
{
  grub_file_t file = 0;
  struct linux_kernel_header lh;
  grub_uint8_t setup_sects;
  grub_size_t real_size;
  grub_ssize_t len;
  int i;
  char *grub_linux_prot_chunk;
  int grub_linux_is_bzimage;
  grub_addr_t grub_linux_prot_target;
  grub_err_t err;

  grub_dl_ref (my_mod);

  if (argc == 0)
    {
      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
      goto fail;
    }

  file = grub_file_open (argv[0]);
         ^^^^^^^^^^^^^^^^^^^^^^^^!! read linux kernel file
  if (! file)
    goto fail;

 -> file openに成功後、612byteのkernel header情報をlhへreadし、version/signatureのチェックを行っている。

```

```c:grub-2.00/grub-core/loader/i386/linux.c-
static grub_err_t
grub_linux_boot (void)
{
...
  state.ebp = state.edi = state.ebx = 0;
  state.esi = real_mode_target;
  state.esp = real_mode_target;
  state.eip = params->code32_start;
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^!! 命令ポインタにLinux Kernelのエントリポイントを設定。
  return grub_relocator32_boot (relocator, state, 0);
}

 o 圧縮Linux Kernelの解凍
   Boot Loaderから物理アドレスLinux Kernelエントリポイントへ制御が移った後、圧縮データ(Linux Kernel)の解凍ルーチンが開始。
   ここからLinux Kernelの仕事になるため、ソースコード的にはGRUBからLinuxへ移動。
```
```s:arch/x86/boot/compressed/head_64.S
	__HEAD
	.code32
ENTRY(startup_32)
	cld
	/*
	 * Test KEEP_SEGMENTS flag to see if the bootloader is asking
	 * us to not reload segments
	 */
	testb $(1<<6), BP_loadflags(%esi)
	jnz 1f

	cli
	movl	$(__KERNEL_DS), %eax
	movl	%eax, %ds
	movl	%eax, %es
	movl	%eax, %ss
1:
...

	/* Target address to relocate to for decompression */
	addl	$z_extract_offset, %ebx
                 ^^^^^^^^^^^^^^^^!! 解凍後のkernelを配置するためのoffset

	/* Set up the stack */
	leal	boot_stack_end(%ebx), %esp

	/* Zero EFLAGS */
	pushl	$0
	popfl

/*
 * Copy the compressed kernel to the end of our buffer
 * where decompression in place becomes safe.
 */
	pushl	%esi
	leal	(_bss-4)(%ebp), %esi
	leal	(_bss-4)(%ebx), %edi
	movl	$(_bss - startup_32), %ecx
	shrl	$2, %ecx
	std
	rep	movsl
	cld
	popl	%esi

/*
 * Jump to the relocated address.
 */
	leal	relocated(%ebx), %eax
	jmp	*%eax
ENDPROC(startup_32)

	.text
relocated:

　　-> ここまでで自分自身(解凍ルーチンと圧縮kernel)をoffset先へ飛ばしている。
     これ以降で圧縮kernelを解凍して、移動前ベースへ配置する。
     CPUモード32->64への切り替えはここでは省略。

/*
 * Clear BSS (stack is currently empty)
 */
	xorl	%eax, %eax
	leal	_bss(%ebx), %edi
	leal	_ebss(%ebx), %ecx
	subl	%edi, %ecx
	shrl	$2, %ecx
	rep	stosl

/*
 * Adjust our own GOT
 */
	leal	_got(%ebx), %edx
	leal	_egot(%ebx), %ecx
1:
	cmpl	%ecx, %edx
	jae	2f
	addl	%ebx, (%edx)
	addl	$4, %edx
	jmp	1b
2:

/*
 * Do the decompression, and jump to the new kernel..
 */
	leal	z_extract_offset_negative(%ebx), %ebp
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^!! minus offsetポイント。
				/* push arguments for decompress_kernel: */
	pushl	%ebp		/* output address */
	pushl	$z_input_len	/* input_len */
	leal	input_data(%ebx), %eax
	pushl	%eax		/* input_data */
	leal	boot_heap(%ebx), %eax
	pushl	%eax		/* heap area */
	pushl	%esi		/* real mode pointer */
	call	decompress_kernel
                ^^^^^^^^^^^^^^^^^!! decompression starts here.
	addl	$20, %esp

  -> ここからstartup_64へjumpしてlinux kernel初期化が本格的に開始。
     (ここまではすべてarch/x86/compressed/配下の処理)
```

[Note/Tips]
BIOS -> Boot Sector(512KB) -> Boot Loader -> Kernel

* 調査はすべてx86コードをベースに行っているので、他のarchは対象外。

主なBoot Loaderは以下。
 o LILO
 o SYSLINUX
 o GRUB/GRUB2