2013年12月6日金曜日

Installing Linux (Yocto) to DE2i-150

今年の夏ごろ(?)からずっとpre-order状態だったDE2i-150がようやく届いた。TerasicのDE2i-150はIntelのAtomプロセッサN2600(cedartrail)とCyclone IV FPGAがPCIリンクにより接続された基板であり、今までデスクトップPCにFPGAが乗ったPCIボードを接続していた構成がそのまま1枚の小さな基板にシュリンクしたようなイメージ(違うかもしれない)。DE2i-150にはあらかじめYocto ProjectのLinuxがインストールされており、とりあえず火入れして起動を確認したが、Terasicのサイトでディスクイメージ(ISOファイル)が配布されているのを知っていたので、おそらくこのディスクイメージからインストールできるのであろうと思い、適当に上書きインストールしてプリインストールのLinuxを早々に潰した。DE2i-150にはmSATA SSD 64GBが搭載されており、ここにLinuxをインストールすることになるのだが、GPTとしてパーティションがフォーマットされるので、そのままでは起動できずにかなり困った。実はインストール直後の状態でも、EFI System Partitionにブートローダは書き込まれており、システム起動時にEFI Shellがあるのでそこから起動させることも可能であるが、デフォルトではBIOSブートになってしまい自動的にEFIブートさせる方法がわからなかったので、GRUBを使ってGPTフォーマットされたパーティションをBIOSブートさせることにした。

インストールの流れは以下の通り。
  1. ISOファイルをUSBに書き込みUSBブート可能にする
  2. インスールが自動的に開始されるのでインストラクションに従う
  3. インストール完了後にリブート
  4. EFI Shellから手動で起動させる
  5. GNU partedをビルドする
  6. GNU partedでBIOS Boot Partitionを作成する
  7. /bootにGRUBをインストールする

ISOファイルをUSBに書き込みUSBブート可能にする

Terasicのサイトからダウンロードできるのはcore-image-sato-cedartrail-20130522214834.isoというディスクイメージであるが、これをUniversal-USB-Installer-1.9.5.1を利用してUSBブート可能にする。Universal USB Installerの使用方法は真面目に調査してないが、Step1では"Try Unlisted Linux ISO"を指定し、Step2では書き込むISOファイルを指定した。

インスールが自動的に開始されるのでインストラクションに従う

USBをDE2i-150に接続した状態で起動し、起動画面が表示されたところでF10を押す。起動メニューが表示されるので、"EFI BUFFALO USB Flash Disk"を選択する。GRUBのメニューからinstallを選択すると、自動的にmSATA SSDが検出されて下記のメッセージが表示されるのでyを選択する。
Found Drive at /dev/sda. Do you want to install this image there ? [y/n]
y
パーティションは/bootと/とswap領域が自動的に作成される。/bootにはカーネルイメージであるvmlinuzがあるので以下の作業を通して、最終的にはここからGRUBを利用して起動することにする。

インストール完了後にリブート

以下のメッセージが表示されたらインストールメディア(USB)を外してEnterにて再起動する。
Remove your installation media, and press ENTER
ただし、このままでは起動しない。本当はEFIブートできるはずなのだが、方法がわからなかったためGRUBを利用することにした。

EFI Shellから手動で起動させる

起動画面にて再びF10を押すとメニューが表示されるのでEFI Shellを選択する。EFI Shellが起動すると表示されるように、fs0がEFI System Partition (/bootにあたる)となっており、ここからEFIブートが可能である。以下のコマンドを実行するとLinuxが無事起動する。
Shell> fs0:
fs0:\> EFI\BOOT\bootia32.efi
なお、起動時にイーサネットを接続しておくと、DHCPでIPアドレスが取得できてネットワークにつながるようになり、外部からSSHで接続できるようになるため非常に便利。インストール直後はrootのパスワードはrootになっているはず。

GNU partedをビルドする

デフォルトではpartedがインストールされていないので、ソースコードからビルドする。ビルドにはlibuuidが必要なのでまず先にlibuuidのインストールから始める。ソースコードはUSBからコピーするか、もしくはインターネットからダウンロードする。
root@cedartrail:~# wget http://ftp.gnu.org/gnu/parted/parted-3.0.tar.gz
root@cedartrail:~# wget http://downloads.sourceforge.net/project/libuuid/libuuid-1.0.2.tar.gz
libuuidはconfigureとmakeで問題なくインストールできる。
root@cedartrail:~# tar zxvf libuuid-1.0.2.tar.gz
root@cedartrail:~# cd libuuid-1.0.2
root@cedartrail:~# ./configure
root@cedartrail:~# make && make install
partedはビルド作業を短縮するためreadline機能を省く。
root@cedartrail:~# tar zxvf parted-3.0.tar.gz
root@cedartrail:~# cd parted-3.0
root@cedartrail:~# ./configure --without-readline
root@cedartrail:~# make
ところが、lib/stdio.hの1050行目でgets()がないというエラーが起きるので、以下のようにfgets()に書き換える。
(修正前)_GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
(修正後)_GL_WARN_ON_USE (fgets, "gets is a security hole - use fgets instead");
これでもまだエラーが起きる。
../libparted/.libs/libparted.so: undefined reference to `dm_task_set_major_minor'
とりあえずconfitureオプションに--disable-device-mapperを付加することでdevice mapperは無効化した。再度、makeを実行するとstdio.hが再生成されるので、先ほどと同様に1050行目のgets()を修正してmakeする。
root@cedartrail:~# ./configure --without-readline --disable-device-mapper
root@cedartrail:~# vi lib/stdio.h
root@cedartrail:~# make && make install

GNU partedでBIOS Boot Partitionを作成する

partedがインストールできたらmSATA SSDである/dev/sdaにBIOS Boot Partitionを作成する。BIOS Boot PartitionというのはGPTフォーマットされたディスクにGRUBをインストールするために必要なGRUB専用のパーティションである。GRUBはMBRの512バイトだけでは容量が足りず、後続のプログラムを置くためにさらにディスク領域を必要とする。従来のMBR方式ではMBRの直後のセクタにそのための領域をとっていたが、GPT方式ではMBRの直後にGPTのプライマリヘッダが配置されてしまうため、GRUBを置くためのスペースが取れない。したがって、BIOS Boot Partitionというものが必要となる。まずは/dev/sdaのパーティション情報を確認する。
root@cedartrail:~# parted /dev/sda
GNU Parted 3.0
Using /dev/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print free
print free
Model: ATA InnoLite mSATA D (scsi)
Disk /dev/sda: 64.0GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system     Name     Flags
        17.4kB  1049kB  1031kB  Free Space
 1      1049kB  19.9MB  18.9MB  fat16           primary
 2      19.9MB  60.8GB  60.8GB  ext3            primary
 3      60.8GB  64.0GB  3201MB  linux-swap(v1)  primary
        64.0GB  64.0GB  335kB   Free Space
パーティションテーブルはGPTでフォーマットされていることが確認できる。Yoctoインストーラによると/bootと/とswap領域が自動的に作成されるが、print freeで見てみると実は/bootの前に1MB程度の空き領域があることがわかる。これをすべてBIOS Boot Partitionに割り当てることにする。
(parted) mkpart primary fat16 17.4kB 1049kB
mkpart primary fat16 17.4kB 1049kB
Warning: The resulting partition is not properly aligned for best performance.
Ignore/Cancel? Ignore
Ignore
(parted) print free
print free
Model: ATA InnoLite mSATA D (scsi)
Disk /dev/sda: 64.0GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system     Name     Flags
 4      17.4kB  1049kB  1031kB                  primary
 1      1049kB  19.9MB  18.9MB  fat16           primary
 2      19.9MB  60.8GB  60.8GB  ext3            primary
 3      60.8GB  64.0GB  3201MB  linux-swap(v1)  primary
        64.0GB  64.0GB  335kB   Free Space

4番目のプライマリパーティションが新規に割り当てられた(File sytemの項目は反映されない?)。しかし、このままではこのパーティションがBIOS Boot PartitionであることをGRUBが認識できない。そのため、以下のようにbios_grubフラグをonにする。
(parted) set 4 bios_grub on
最終的に以下のような構成になる。
(parted) print free
print free
Model: ATA InnoLite mSATA D (scsi)
Disk /dev/sda: 64.0GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system     Name     Flags
 4      17.4kB  1049kB  1031kB                  primary  bios_grub
 1      1049kB  19.9MB  18.9MB  fat16           primary
 2      19.9MB  60.8GB  60.8GB  ext3            primary
 3      60.8GB  64.0GB  3201MB  linux-swap(v1)  primary
        64.0GB  64.0GB  335kB   Free Space

/bootにGRUBをインストールする

BIOS Boot Partitionを作成したのでGPTフォーマットされた/dev/sdaにもGRUBをインストールできるようになった。grub-installで/bootにGRUBのcore.imgや設定ファイルをインストールするため、まずは/bootパーティションをマウントする。/bootパーティションは/dev/sda1であることに注意。
root@cedartrail:~# mkdir /mnt/boot
root@cedartrail:~# mount /dev/sda1 /mnt/boot
そして、targetにi386-pc、boot-directoryに/mnt/bootを指定して/dev/sdaにgrub-installする。
root@cedartrail:~# grub-install --target=i386-pc --boot-directory=/mnt/boot /dev/sda
Installation finished. No error reported.
ここで一旦再起動してGRUBが起動することを確認する。まだGRUBの設定ファイルを用意していないので、GRUBのシェルが起動するだけである。lsでパーティション情報を表示させると以下のようになる。
grub> ls
(hd0) (hd0,gpt4) (hd0,gpt3) (hd0,gpt2) (hd0,gpt1)
ルートファイルシステムは(hd0,gpt2)であるが、カーネルイメージは/bootパーティションである(hd0,gpt1)にあることに注意する。したがって、とりあえず起動するには以下のように指定する。(GRUBは/dev/sda1にインストールしたcore.imgで起動しているので、/vmlinuzの/はこの場合/dev/sda1を示している。)
grub> linux /vmlinuz root=/dev/sda2 rw
起動後は解像度が低いがビデオの設定をしていないため。GRUBの設定ファイルは/bootディレクトリに用意されているので、これをコピーすれば設定は完了。
root@cedartrail:~# mount /dev/sda1 /mnt/boot
root@cedartrail:~# cat /mnt/boot/EFI/BOOT/grub.cfg
# Automatically created by OE
serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
default=boot
timeout=10

menuentry 'boot'{
linux /vmlinuz  root=/dev/sda2 rw  quiet    video=LVDS-1:d console=ttyS0,115200 console=tty0
}
root@cedartrail:~# cp /mnt/boot/EFI/BOOT/grub.cfg /mnt/boot/grub/
以上で正常にLinuxが起動するようになる。

というか

Terasicは配布物をちゃんとドキュメント化して欲しいです。。

参考資料
http://www.gnu.org/software/grub/
http://www.gnu.org/software/parted/