Linux Boot Sequence を読む ( 5 ) ~ 64 bit モード移行/圧縮カーネルの解凍 ~

February 19, 2018

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 モード移行のための呪文)

  1. ページングの無効化
  2. 物理アドレス拡張機構 (PAE) の有効化
  3. ページングテーブルのロード
  4. IA-32e モードの有効化
  5. ページングの有効化
  6. 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) &amp; 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;  
}  

 © 2023, Dealing with Ambiguity