ykominami
9/17/2011 - 2:25 AM

QEMU: 仮想ボード ARM Cortex-A9 マルチコアで Linux を動かす

QEMU: 仮想ボード ARM Cortex-A9 マルチコアで Linux を動かす

  
モチベーション
---------------
 Androidのエミュレータは中でQEMUを使ってる
 中で使われているので、Android開発とは直接関係がないけど、
 素のQEMUの使い方を知る。
  
 * QEMUの使い方を覚える
 * Linuxをエミュレータ環境で動かしてみる
 * ARM のクロス環境に慣れてみる
 * ARM はマルチコア
  

環境
-----
Linux 2.6.38-11-generic Ubuntu SMP 
kernel-v2.6.39
QEMU emulator version 0.14.0 (qemu-kvm-0.14.0)


linux kernelをコンパイル
-------------------------
$ cd kernel-v2.6.39

$ export ARCH=arm

$ export CROSS_COMPILE=arm-none-linux-gnueabi-
 クロス環境のツールチェインはCodeSourceryのツールチェインを使用する
 https://sourcery.mentor.com/sgpp/lite/arm/portal/subscription?@template=lite
 target OS GNU/LinuxをダウンロードしてbinにPATHを通す.

$ make realview_smp-defconfig
  ターゲット向けのコンフィグレーションファイルを作る。
  あらかじめ用意されているコンフィグレーションがあるので(XXX-defconfig)を
  それを指定する. 今回はSMPなので realview_smp-defconfigを指定.
  ターゲットのコンフィグレーションファイルの種類はARMの場合,arch/arm/configsで確認できる。

$ make menuconfig
 ARMv7アーキテクチャに設定する、またCortex-A9を使用する.
 
    * loadable module support [disabled]
    * Platform Baseboard for ARM11 [disabled]
    * Support ARM ARMv6 [disabled]
    * PBX A9 and ARMv7 [enabled] 
       -> Support RealView(R) Emulation Baseboard
         -> Support Multicore Cortex-A9 Tile
    * Support RealView(R) Platform Baseboard Explore [enabled]
    * CONFIG_REALVIEW_HIGHT_PHYS_OFFSET [disabled]
      RealView platform type
        -> Hight physical base address for the RealView platform
        
    * EABI(allowing also old ABI) SupportがEnabledになっているかを確認すること
      これはCodeSourcery toolchainでコンパイルするので必要
      
    ** Network device support 
    	-> Network Ethernet (10 or 100Mbit) 
    	->SMSC LAN911X [disabled] 
      qemu-0.14.0でハードフェアエラーがでるためdisabledにする
      
      
ビルドが成功するとarch/arm/boot/zImageにカーネルイメージが出来上がる
このカーネルイメージはQemuで実行することができる


Kernel の起動を確認する
------------------------

シングルコアでの起動を確認
qemu-system-arm -M realview-pbx-a9 -m 128M -kernel kernel-v2.6.39/arch/arm/boot/zImage -nographic -append "console=ttyAMA0 mem=128M"

マルチコアでの起動を確認
qemu-system-arm -M realview-pbx-a9 -m 128M -kernel kernel-v2.6.39/arch/arm/boot/zImage -nographic -smp 2 -append "console=ttyAMA0 mem=128M"

initrd(ルートファイルシステム)を指定していないので、途中でエラーになるのが正しい

 -M         : 仮想ボードのモデル名
 -m         : メモリ容量
 -kernel    : linux kernelのイメージ
 -nographic : windowでなくconsoleで
 -append    : カーネル起動パラメータを指定する
 -smp       : CPUコアの数
 
 Note:
  モデル名は qemu-system-arm -M ? でリストが表示される
  
  QEMUを抜けるには Ctrl-A, x を順番にタイプする。
   

確認用のinitrdの作成
---------------------
確認のためにinitrdを作成し、正しく起動するかを確認してみる
普通のlinuxであると、ルートファイルシステムはさまざまなディレクトリ(/bin, /etc, /usr等) がある
が、これは後回しで、簡単なプログラムを1つ作成して、1ファイルを含むだけのinitrdを作ってみる.

-- test.c

#include <stdio.h>

void main()
{
	printf("Welcome world\n");
	while(1);
}

----

 Note:
   while(1) でループしているのは、linuxがこのプログラムを初めに動かすので、プログラムを
   終了させないため


$ arm-none-linux-gnueabi-gcc -static test.c -o test
$ echo test | cpio -o --format=newc > rootfs
$ qemu-system-arm -M realview-pbx-a9 -m 128M -kernel kernel-v2.6.39/arch/arm/boot/zImage -nographic -initrd rootfs -smp 2 -append "root=/dev/ram rdinit=/test console=ttyAMA0 mem=128M"

Note:
 cpioはfilesystemを作るのに使われる。cpioはファイルのリストを入力しアーカイブを出力する。
 formatにnewcを指定するとinitramfs形式のフォーマット。
 いま作成したrootfsはtestバイナリファイルを1つ含んだファイルシステムイメージで、これを
 QEMUのinitrdパラメータに渡すことができる。
 
 -initrd でinitrd を指定する
 -append でrdinit=に初めに実行するプログラムを指定できる
 
上手く動いていれば "Welcom world" が最後に出力される。


BusyBoxを使用してツールを作成する, initrdも作成
------------------------------------------------
上記でlinuxは起動できたが、いろいろツールが足りないので、BusyBoxでターゲット用の
各種ツール群を作成する。
BusyBoxはコンパクトなファイルシステムを必要とする組み込みlinuxでよくつかわれる、
さまざまなシステムユーティリティを提供する。

busybox-1.19.2
http://www.busybox.net/

$ export ARCH=arm
$ export CROSS_COMPILE=arm-none-linux-gnueabi-

$ make defconfig

$ make menuconfig
 デフォルトのコンフィグレーションではかなりの数のツールが選択されているので、ほしいものだけ
 をチェックする。コンパイル途中でエラーがでたら素直に外すのが吉
 
$ make install

ビルドが成功すると _install/ にルートファイルシステムのツリーが生成される。
これらを前回の"Welcome world"と同じように、cpioツールを使用してlinuxのルートファイル
システムイメージを作ると出来上がり。

$ cd _install
$ find . | cpio -o --format=newc > ../rootfs.img
$ cd ..
$ gzip -c rootfs.img > rootfs.img.gz

$ qemu-system-arm -M realview-pbx-a9 -m 128M -kernel kernel-v2.6.39/arch/arm/boot/zImage -nographic -initrd rootfs.img.gz -smp 2 -append "root=/dev/ram rdinit=/bin/sh console=ttyAMA0 mem=128M"

rdinit=で/bin/shを指定しているのでシェルが立ち上がる。ls, pwd等のコマンドが使えるようになっている。
一方今のままでは、psコマンドが使えない。これは特殊なファイルシステムが/procが必要なので作成する必要がある。

$ mkdir /proc
$ mount -t proc none /proc

また、/devがconsoleデバイスを除いて、空っぽである。こちらも特殊なファイルシステム/sysを作成する

$ mkdir /sys
$ mount -t sysfs none /sys
$ mdev -s

これらのマウント処理は通常、起動時に/sbin/initにさせることができる。
/sbin/initは通常linux kernelによって最初に起動されるプログラムである。
initに何かをさせるには/etc/init.d/rcSに記述する。
なのでBusyBoxで_installを作成したあとに各種設定ファイルを追加して、cpio -> gzip でinitrdを作れば
いいだろう

$ cd _install
$ mkdir proc sys dev etc etc/init.d
$ cd ..

-- _install/etc/init.d/rcS
#!/bin/sh

mount -t proc none /porc
mount -t sysfs none /sys
/sbin/mdev -s

---

$ chmod +x _install/etc/init.d/rcS

この内容で、またrootfs.img.gzを作成
今回は、rdinit=/sbin/initを指定する
$ qemu-system-arm -M realview-pbx-a9 -m 128M -kernel kernel-v2.6.39/arch/arm/boot/zImage -nographic -initrd rootfs.img.gz -smp 2 -append "root=/dev/ram rdinit=/sbin/init Console=ttyAMA0 mem=128M"


appendix
---------
QEMUでは-kernelオプションに渡されたバイナリファイルを0x00010000にロードする。
エミュレータは0x00000000番地からスタートするが、ここには予めいくつかのインストラクションが
含まれており、最終的に0x00010000にジャンプするようになっている。このことから
あるプログラムをQEMU上で単に実行するには、0x00010000でリンクされたバイナリを作成することで
実行できる

startup.o
------------
.global _Reset
_Reset:
  LDR sp, =stack_top
  BR c_entry
  B .

test.ld
--------
ENTRY(_Reset)
SECTIONS
{
  . = 0x10000;
  .startup . : { startup.o(.text) }
  .text : { *(.text) }
  .data : { *(.data) }
  .bss : { *(.bss) }
  . = . + 0x1000;
  stack_top = .;
}

-----
$ arm-none-eabi-as -mcpu=arm926ej-s -g startup.s -o startup.o
$ arm-none-eabi-gcc -c -mcpu=arm926ej-s -g test.c -o test.o
$ arm-none-eabi-ld -T test.ld test.o startup.o -o test.elf
$ arm-none-eabi-objcopy -O binary test.elf test.bin

 

-----
arch/arm/mach-<mach>/Makefile.boot
zreladdr: head.SがRAMにzImageを展開したあとのKernelのスタートアドレス