Linux Kernel
GRUB Kernel から Linux Kernel へ処理が譲渡されるため、ここからは Linux Kernel を読むことになる。
今回利用したカーネルは以下のように SRPM を取得している。
wget http://vault.centos.org/7.3.1611/updates/Source/SPackages/kernel-3.10.0-514.21.2.el7.src.rpm
64bit モードへの移行/圧縮されたカーネルの解凍
GRUB の grub_linux_boot() から Linux Kernel に処理が回ってきた後に最初に実行されるのは、圧縮された Linux Kernel の解凍ルーチンとなる。
実際に解凍ルーチンを実行する部分の処理は arch/x86/boot/compressed の haed_64.S と misc.c で行われる。head_64.S では解凍だけではなく、CPU を 32bit モードから 64bit モードに移行する処理も実行されることとなる。
64bit モードへ移行するには以下の手順を順番に実行する。(64bit モード移行のための呪文)
- ページングの無効化
- 物理アドレス拡張機構 (PAE) の有効化
- ページングテーブルのロード
- IA-32e モードの有効化
- ページングの有効化
- 64bit コードセグメントへのジャンプ
圧縮カーネルの解凍は head64.S の以下の部分で decompress\kernel() を呼び出すことで実行している。
/*
* Do the decompression, and jump to the new kernel..
*/
pushq %rsi /* Save the real mode argument */
movq %rsi, %rdi /* real mode address */
leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
leaq input_data(%rip), %rdx /* input_data */
movl $z_input_len, %ecx /* input_len */
movq %rbp, %r8 /* output target address */
call decompress_kernel
popq %rsi
decompress_kernel() は arch/x86/boot/compressed/misc.c に定義されており、decompress(…) の部分で実際の解凍処理が走る。
実際は decompress() 内で lib/decompress_inflate.c に定義される gunzip() により gzip フォーマットの kernel が解凍される運びとなる。
asmlinkage void decompress_kernel(void *rmode, memptr heap,
unsigned char *input_data,
unsigned long input_len,
unsigned char *output)
{
... (中略) ...
console_init();
debug_putstr("early console in decompress_kernel\n");
free_mem_ptr = heap; /* Heap */
free_mem_end_ptr = heap + BOOT_HEAP_SIZE;
if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
error("Destination address inappropriately aligned");
#ifdef CONFIG_X86_64
if (heap > 0x3fffffffffffUL)
error("Destination address too large");
#else
if (heap > ((-__PAGE_OFFSET-(128<<20)-1) & 0x7fffffff))
error("Destination address too large");
#endif
#ifndef CONFIG_RELOCATABLE
if ((unsigned long)output != LOAD_PHYSICAL_ADDR)
error("Wrong destination address");
#endif
debug_putstr("\nDecompressing Linux... ");
decompress(input_data, input_len, NULL, NULL, output, NULL, error);
parse_elf(output);
debug_putstr("done.\nBooting the kernel.\n");
return;
}