Linux を(わりと)シンプルな構成でビルドして Qemu で起動する

More than 3 years have passed since last update.


はじめに


  • 唐突に小さいカーネルを何となく作りたくなりました(。>﹏<。)

  • そこで(わりと)シンプルな kernel config で Linux カーネルを作れるか試してみました

  • まったり進行です


前提


  • ビルド環境は Ubuntu 14.04 64 bit を使っています


    • 64 bit 環境で 32 bit のカーネルをビルドする、とか需要あるのかしら。

    • あれば書こうかなぁ。



  • init は自作の Hello, world で代用します

  • ディスクイメージ(qcow2とか)に焼く方法は書いてません。qemu の -kernel オプションと -initrd オプションを使ってビルドしたカーネルを起動します


    • そのうち書くかも




ざっくりとした流れ


  1. qemu をインストールして

  2. カーネルを設定して、ビルドして

  3. initramfs を作って

  4. qemu で起動(∩´∀`)∩


下準備


使うツールのインストール


  • qemu と カーネルの設定をする際に使う make menuconfig で利用される libcurses 群を入れます

$ sudo apt-get install qemu

$ sudo apt-get install libncurses5 libncurses5-dev


カーネルのダウンロードと解凍

$ wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.4.tar.xz

$ tar Jxvf linux-4.4.4.tar.xz
$ cd linux-4.4.4


カーネルの設定


カーネルに必要最低限なコンフィグのみ残す

$ make allnoconfig


  • どれくらい行数があるか気になったのでカウントしてみました

$ egrep -v "(^#|^$)" .config | wc -l

234


  • 結構ありますね(* ´﹃` *)


qemu で起動するために必要なコンフィグを有効にする


  • make menuconfig を使います。

$ make menuconfig


  • 下の項目を有効にしていきます。

[*] 64-bit kernel

-> General setup
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
-> General setup
-> Configure standard kernel features
[*] Enable support for printk

-> Executable file formats / Emulations
[*] Kernel support for ELF binaries

-> Device Drivers
-> Character devices
[*] Enable TTY

-> Device Drivers
-> Character devices
-> Serial drivers
[*] 8250/16550 and compatible serial support
[*] Console on 8250/16550 and compatible serial port

設定項目
何に使うか

64-bit kernel
今回は 64 bit kernel をビルドするので必要です

Initial RAM filesystem and RAM disk (initramfs/initrd) support
initramfs/initrd を有効にします。qemu から -initrd オプションで起動させるためです。これを有効にしないでやるケースってどんなときなんでしょうね。

Enable support for printk
printk 出力を有効にします。カーネルの起動ログをコンソールに出すようにするためです。

Kernel support for ELF binaries
ELF を読み込めるようにします。initrd に指定するプログラムが ELF binary なためです。

Enable TTY
TTY を有効にします。ターミナルに文字を出力させるためです。

Console on 8250/16550 and compatible serial port
シリアルポートを有効にします。カーネルの出力を見られるようにするために必要です


  • ここでコンフィグの行数をカウントしてみます。

$ egrep -v "(^#|^$)" .config | wc -l

311


  • make allnoconfig が 234 だったので、思ったより増えてますね。


カーネルのビルド



  • -j オプションはマルチコアの場合に並列ビルドをします。任意で指定してくださいませ。

$ make -j4 bzImage


  • ビルドを待ちます。この時にビルド対象のコード一覧を見てみるのも楽しいかもしれません

  • bzImage ができていることを確認します。今回は 64 bit kernel なので  ./arch/x86/boot/bzImage を使います

$ find ./ -name bzImage

./arch/i386/boot/bzImage
./arch/x86/boot/bzImage


  • カーネルができあがったので、次に initramfs を作ります。


initramfs の作成


下準備(作業ディレクトリの作成とか)

$ mkdir ~/work

$ cd ~/work
$ cp ~/linux-4.4.4/arch/x86/boot/bzImage bzImage


init で実行するプログラムの作成


  • おもむろに vim を立ち上げて init.c ファイルを作ります

$ vim init.c


  • 以下のようなコードを書きます

#include <stdio.h>


int main(void) {
printf("-----------HELLO------------\n");
return 0;
}


  • libc をプログラム本体に含めるために static ビルドをします

$ gcc -static -o init init.c


プログラムを gzip 圧縮の initramfs 形式にする


  • cpio コマンドがパイプでプログラム名を受け取れるため、以下のようなコマンドを実行して、initramfs を作ります

$ echo init | cpio -o -H newc | gzip > initramfs.cpio.gz


カーネルの起動


  • これですべての準備が整いました。以下のコマンドを実行してカーネルを立ち上げてみてください

$ qemu-system-x86_64 -kernel ./bzImage -initrd ./initramfs.cpio.gz -append "console=ttyS0" -nographic


  • 以下のような感じでカーネルの起動ログが出て、作成したプログラムの HELLO という文字が出力されていると思います


    • panic してますが、とりあえずここまで。



(省略)

mousedev: PS/2 mouse device common for all mice
input: AT Translated Set 2 keyboard as /devices/platform/i8042/serio0/input/input0
Freeing unused kernel memory: 440K (ffffffff81175000 - ffffffff811e3000)
-----------HELLO------------
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000

Kernel Offset: disabled
---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000