NetBSDカーネルをビルドしてみる

# NetBSDカーネルをビルドしてみる

今日のNetBSD Advent Calendarでは、NetBSDカーネルのビルド手順について書いてみたいと思います。

## NetBSDカーネルソースコードを用意する

### ソースCD(ISOイメージ)の入手

まずはカーネルのソースコードを用意します。NetBSDのインストールCD-ROM配布サイトから、ソースコード一式が入ったISOイメージをダウンロードします。

 * ftp://iso4.jp.NetBSD.org/pub/NetBSD/iso/6.1.5/NetBSD-6.1.5-source.iso

ダウンロードしたNetBSD-6.1.5-source.isoを、仮想マシンのCD-ROMとして接続します。

![img012.png](https://qiita-image-store.s3.amazonaws.com/0/61137/076f0842-a608-0554-58e6-609c7e5ba81d.png)

あとは普通にNetBSDからmountするだけでOKです。

### ソースファイルを展開する

```sh
$ sudo mount /dev/cd0a /mnt
$ mount | grep mnt
/dev/cd0a on /mnt type cd9660 (read-only, local)
$ df -h | grep mnt
/dev/cd0a          432M       432M         0B 100% /mnt
```

ソースファイルは.tgzの形で格納されています。

```sh
$ find /mnt/ | grep \.tgz$
/mnt/source/sets/gnusrc.tgz
/mnt/source/sets/sharesrc.tgz
/mnt/source/sets/src.tgz
/mnt/source/sets/syssrc.tgz
/mnt/source/sets/xsrc.tgz
```

各.tgzファイルは、カレントディレクトリから見て./usr/以下に展開される形になっています。

```sh
$ for i in `find /mnt/ | grep \.tgz$`;do tar ztvf $i | head -n1; done 2> /dev/null
drwxrwxr-x  2 root     wsrc           0 Sep 29 16:17 usr/src/gnu
drwxrwxr-x  2 root     wsrc           0 Sep 29 16:17 usr/src/share
drwxrwxr-x  2 root     wsrc           0 Sep 29 16:17 usr/src
drwxrwxr-x  2 root     wsrc           0 Sep 29 16:17 usr/src
drwxrwxr-x  2 root     wsrc           0 Sep 29 16:17 usr/xsrc
```

慣習的にソースコードの展開先は/usr/src/なので、それに倣い展開します。tarのオプションで"-C /"を指定することで、展開先のディレクトリを指定できます。

rootユーザで以下のコマンドを実行し、ソースファイル一式を展開します。

```sh
$ sudo -i
kimiuso# for i in `find /mnt/ | grep \.tgz$`; do tar zxvf $i -C / ; done
```

ソースファイルが展開された後の/usr/src/ディレクトリは以下のようになります。ソースファイル全体のサイズは1.6GBほどのようですね。

```sh
$ ls -F /usr/src/
BUILDING        common/         external/       regress/        usr.bin/
CVS/            compat/         extsrc/         rescue/         usr.sbin/
Makefile        crypto/         games/          sbin/           x11/
Makefile.inc    dist/           gnu/            share/
UPDATING        distrib/        include/        sys/
bin/            doc/            lib/            tests/
build.sh*       etc/            libexec/        tools/
$ du -h /usr/src/ | tail -n1
1.6G    /usr/src/
```

### ISOイメージをCDのようにmountする

蛇足になりますが、ISOイメージ自体をmountすることも可能です。場合によってはこちらの方が手順的には楽かもしれません。

vnconfigコマンドにより、ISOイメージを擬似的なディスクデバイスに見せかけ、それをmountするという形になります。
(Linuxだとmount -o loopbackとかだったような...)

```sh
$ ls NetBSD-6.1.5-source.iso
NetBSD-6.1.5-source.iso
$ sudo vnconfig vnd0 NetBSD-6.1.5-source.iso
$ sudo vnconfig -l
vnd0: / (/dev/wd0a) inode 1562384
vnd1: not in use
vnd2: not in use
vnd3: not in use
$ sudo mount -t cd9660 /dev/vnd0a /mnt
$ mount | grep mnt
/dev/vnd0a on /mnt type cd9660 (read-only, local)
$ df -h | grep mnt
/dev/vnd0a         432M       432M         0B 100% /mnt
```

mountしてしまえば、後の手順はCD-ROMと同じです。使い終わったら、以下の手順でunmountし、疑似デバイスからも外します。

```sh
$ sudo umount /mnt
$ sudo vnconfig -u vnd0
$ sudo vnconfig -l
vnd0: not in use
vnd1: not in use
vnd2: not in use
vnd3: not in use
```

## カーネルのビルド

では早速、展開したソースファイルからNetBSDカーネルをビルドしてみます。

まず、カーネルビルド時に組み込むデバイスやオプションを指定するためのファイルとして、カーネルコンフィギュレーションファイルというものがあります。

長ったらしい呼び名なので、単に「configファイル」と呼ばれることもあります。以降は「configファイル」という表記で説明します。

例えばNetBSD-amd64の場合のconfigファイルは/usr/src/sys/arch/amd64/conf/にあります。NetBSDは複数のCPUアーキテクチャで動作するので、arch/{CPUアーキテクチャ名}の下にconfigが置かれている構成になっています。

```sh
$ cd /usr/src/sys/arch/amd64/conf/
$ ls -F
CVS/                Makefile.amd64      kern.ldscript       majors.amd64
GENERIC             XEN3_DOM0           kern.ldscript.2MB   std.amd64
INSTALL             XEN3_DOMU           kern.ldscript.Xen   std.xen
INSTALL_XEN3_DOMU   files.amd64         largepages.inc
```

インストールCDでは「GENERICカーネル」という、一般的なカーネル構成でビルドされたカーネルがインストールされます。GENERICカーネルのconfigファイルは、その名前の通り、GENERICというファイル名になっています。

このGENERICなconfigファイル、コメントにいろいろと書かれており、その部分をgrepするだけで、どんな機能やドライバがあるかが何となく見えてきます。

```sh
$ grep '^# ' GENERIC | egrep -i '(controller|device|support)'        
# and device drivers, but should be useful for most applications.
# For further information on hardware support for this architecture, see
# of each device driver in this file see the section 4 man page for the
# device.
# Diagnostic/debugging support options
# Wedge support
# console scrolling support.
# enable splash screen support; requires genfb or radeonfb
# Device configuration
# ACPI devices
# Mainboard devices
# Basic Bus Support
# PCI bus support
# ISA bus support
# CardBus bridge support
# CardBus bus support
# Console Devices
# Cryptographic Devices
# PCI cryptographic devices
# Serial Devices
# AMD 768 and 8111 power/ACPI controllers
# NVIDIA nForce2/3/4 SMBus controller
# Intel PIIX4 power management controllers
# Intel ICH SMBus controller
# Thermal monitor and fan controller
# iTE IT87xxF Super I/O with watchdog and sensors support
# I2O devices
# GPIO devices
# 1- Wire support
# I2C support
# Keylock support
# SCSI Controllers and Devices
# PCI SCSI controllers
# PCMCIA SCSI controllers
# SCSI bus support
# SCSI devices
# RAID controllers and devices
# IDE and related devices
# PCI IDE controllers - see pciide(4) for supported hardware.
# a machine hang with some controllers.
# PCMCIA IDE controllers
# CardBus IDE controllers
# ISA ST506, ESDI, and IDE controllers
# Some controllers pass the initial 32bit test, but will fail later.
# ATA (IDE) bus support
# Flags are used only with controllers that support DMA operations
# and mode settings (e.g. some pciide controllers)
# 0x0000 means "use whatever the drive claims to support".
# ATAPI bus support
# ATA RAID configuration support, as found on some Promise controllers.
# ATAPI devices
# Miscellaneous mass storage devices
# MII/PHY support
# USB Controller and Devices
# PCI USB controllers
# CardBus USB controllers
# ISA USB controllers
# PCMCIA USB controllers
# USB bus support
# USB HID device
# USB Generic HID devices
# IrDA and Consumer Ir devices
# PCI IEEE1394 controllers
# CardBus IEEE1394 controllers
# Audio Devices
# PCI audio devices
# Audio support
# MIDI support
# FM-Radio devices
# PCI radio devices
# Radio support
# Video capture devices
# Bluetooth Controller and Device support
# Bluetooth PCMCIA Controllers
# Bluetooth SDIO Controllers
# Bluetooth USB Controllers
# Bluetooth Device Hub
# Bluetooth HID support
# Bluetooth Audio support
# SD/MMC/SDIO Controller and Device support
# SD/MMC controller
# devices (watchdog timer, etc.)
# Virtio devices
# Pseudo-Devices
# disk/mass storage pseudo-devices
# network pseudo-devices
# miscellaneous pseudo-devices
# a pseudo device needed for Coda       # also needs CODA (above)
# a pseudo device needed for SMBFS
# wscons pseudo-devices
# pseudo audio device driver
# a pseudo device needed for veriexec
```

何はともあれ、このGENERICなconfigファイルを元にカーネルをビルドしてみます。単にGENERICファイルを使うのは面白味にかけるので、例によって[四月は君の嘘](http://www.kimiuso.jp "四月は君の嘘")になぞらえたKIMIUSOコンフィグでビルドしてみましょう(と言っても、単にGENERICファイルをコピーするだけですが...)。

まずはKIMIUSOコンフィグを用意します。単にGENERICをcpするだけ。

```sh
$ sudo -i
kimiuso# cd /usr/src/sys/arch/amd64/conf/
kimiuso# cp GENERIC KIMIUSO
```

ここからカーネルビルドの一連の手順です。"config <コンフィグファイル>"を実行すると、ビルドに必要なディレクトリが生成されます。

"make depend"でconfigファイルに合わせたビルド用の構成にして、makeでカーネルをビルドするという流れになります。

```sh
kimiuso# config KIMIUSO
Build directory is ../compile/KIMIUSO
Don't forget to run "make depend"
kimiuso# cd ../compile/KIMIUSO/
kimiuso# make depend
kimiuso# make 2>&1 | tee _make.20141205.0.log
```

これでサクッとNetBSDカーネルができあがります。

```sh
kimiuso# make
...中略...
#      link  KIMIUSO/netbsd
ld -Map netbsd.map --cref -T ../../../../arch/amd64/conf/kern.ldscript -Ttext 0xffffffff80100000 -e start -z max-page-size=0x100000 -X -o netbsd ${SYSTEM_OBJ} ${EXTRA_OBJ} vers.o
NetBSD 6.1.5 (KIMIUSO) #0: Thu Dec  4 22:37:11 UTC 2014
   text    data     bss     dec     hex filename
11734705         632104  645216 13012025         c68c39 netbsd

kimiuso#
```

ビルドが完了すると、"netbsd"という名前でカーネルが生成されます。

```sh
kimiuso# find . -name netbsd
./netbsd
kimiuso# file netbsd
netbsd: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, for NetBSD 6.1.5, not stripped
kimiuso# ls -lh netbsd
-rwxr-xr-x  1 root  wheel   14M Dec  4 22:37 netbsd
```

後は"make install"でカーネルを置き換えるのですが、今回は単にコピーしてみましょう。マシンのブート時に、ルートパーティションにあるカーネルを参照するので、/netbsd.kimiusoという名前でコピーしておきます。

```sh
kimiuso# cp -pi netbsd /netbsd.kimiuso
```

## カーネルソースを編集してブート時に好きな文言を表示してみる

NetBSDカーネルのビルド手順が分かったので、せっかくだからカーネルソースコードもちょっと触ってみましょう。

ブート時に何か好きなメッセージを表示するくらいであれば、比較的簡単に実現できそうです。

NetBSDカーネルのmain()は、init_main.cにあります。

```sh
kimiuso# cd /usr/src/sys/
kimiuso# vi kern/init_main.c
```

init_main.cの中に、banner()という関数があり、ここで起動時に表示するメモリサイズ等を出力しているようです。

```sh
kern/init_main.c:
   1110 /*
   1111  * Print the system start up banner.
   1112  *
   1113  * - Print a limited banner if AB_SILENT.
   1114  * - Always send normal banner to the log.
   1115  */
   1116 #define MEM_PBUFSIZE    sizeof("99999 MB")
   1117
   1118 void
   1119 banner(void)
   1120 {
   1121         static char notice[] = " Notice: this software is "
   1122             "protected by copyright";
   1123         char pbuf[81];
   1124         void (*pr)(const char *, ...);
   1125         int i;
   1126
   1127         if ((boothowto & AB_SILENT) != 0) {
   1128                 snprintf(pbuf, sizeof(pbuf), "%s %s (%s)",
   1129                     ostype, osrelease, kernel_ident);
   1130                 printf_nolog("%s", pbuf);
   1131                 for (i = 80 - strlen(pbuf) - sizeof(notice); i > 0; i--)
   1132                         printf(" ");
   1133                 printf_nolog("%s\n", notice);
   1134                 pr = aprint_normal;
   1135         } else {
   1136                 pr = printf;
   1137         }
   1138
   1139         memset(pbuf, 0, sizeof(pbuf));
   1140         (*pr)("%s%s", copyright, version);
   1141         format_bytes(pbuf, MEM_PBUFSIZE, ctob((uint64_t)physmem));
   1142         (*pr)("total memory = %s\n", pbuf);
   1143         format_bytes(pbuf, MEM_PBUFSIZE, ctob((uint64_t)uvmexp.free));
   1144         (*pr)("avail memory = %s\n", pbuf);
   1145 }
```

[一昨日の記事](http://qiita.com/furandon_pig/items/b1607d8c5bc314c0e31d "プログラミング環境としてNetBSDを使う話")のように、何か表示させてみましょう。変更内容は以下の通りです。

```sh
kimiuso# diff -u kern/init_main.c.orig kern/init_main.c
--- kern/init_main.c.orig       2013-03-14 16:33:10.000000000 +0000
+++ kern/init_main.c    2014-12-04 23:31:25.000000000 +0000
@@ -1142,4 +1142,5 @@
        (*pr)("total memory = %s\n", pbuf);
        format_bytes(pbuf, MEM_PBUFSIZE, ctob((uint64_t)uvmexp.free));
        (*pr)("avail memory = %s\n", pbuf);
+       (*pr)("Your lie in April.\n");
 }
```

これを先ほど同じ手順でビルドします。

```sh
kimiuso# cd /usr/src/sys/arch/amd64/conf/
kimiuso# config KIMIUSO
kimiuso# cd ../compile/KIMIUSO/
kimiuso# make depend
kimiuso# make 2>&1 | tee _make.1.log
```

念のため中身を確認してみます。それっぽい文字列が入っているようですね。

```sh
kimiuso# strings ./netbsd | grep 'Your lie'
Your lie in April.
```

生成されたカーネルファイルを/netbsd.kimiusoとしてコピーします。

```
kimiuso# cp -pi ./netbsd /netbsd.kimiuso
```

### 起動してみる

仮想マシンを再起動すると、ブート直後に以下の画面が表示されます。ここでスペースキーを押すことで、起動時のオプションを選択することができます。

![img013.png](https://qiita-image-store.s3.amazonaws.com/0/61137/70b3f042-d1ce-1b73-aabe-84417f5ed91b.png)

"5. Drop to boot prompt"を選ぶと、プロンプトに落ちます。

![img014.png](https://qiita-image-store.s3.amazonaws.com/0/61137/7850c568-cba3-0f29-9568-245e327867da.png)

"help"と入力すると、利用可能なコマンド一覧が表示されます。"boot"というコマンドにカーネルファイル名を指定することで、任意のカーネルで起動できそうです。

![img015.png](https://qiita-image-store.s3.amazonaws.com/0/61137/1c9c6a83-3595-4316-20dd-b1855304a160.png)

以下のように入力してみます。

```shell-session
> boot netbsd.kimiuso
```

すると、起動時にそれらしき文字列が！

![img016.png](https://qiita-image-store.s3.amazonaws.com/0/61137/39a7fc39-2872-38b3-e4cd-4dfa954d7900.png)

ログインしてuname -aとか実行してみます。KIMIUSOコンフィグによるカーネルであることが分かります。

```sh
-bash-2.05b$ uname -a
NetBSD kimiuso 6.1.5 NetBSD 6.1.5 (KIMIUSO) #1: Thu Dec  4 22:59:42 UTC 2014  root@kimiuso:/usr/src/sys/arch/amd64/compile/KIMIUSO amd64
```

dmesgも見てみます。ばっちり"Your lie in April."という出力が記録されていて、幸せな気分になれます。

```sh
$ dmesg | head -n11
Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
    2006, 2007, 2008, 2009, 2010, 2011, 2012
    The NetBSD Foundation, Inc.  All rights reserved.
Copyright (c) 1982, 1986, 1989, 1991, 1993
    The Regents of the University of California.  All rights reserved.

NetBSD 6.1.5 (KIMIUSO) #1: Thu Dec  4 22:59:42 UTC 2014
        root@kimiuso:/usr/src/sys/arch/amd64/compile/KIMIUSO
total memory = 511 MB
avail memory = 481 MB
Your lie in April.
```

## まとめ

今日のNetBSD Advent Calendarでは、カーネルのビルド手順をつらつらと書いてみました。カーネルのビルドは意外と簡単、と感じてもらえると嬉しいです。