( ..)φカキカキ armで遊んでみる on Fedora

ARMにも興味がある今日この頃、ARMで学ぶ アセンブリ言語入門
という本を買ってみました。
特に読みたかったのはチャプター7の例外と割り込みの所ですねー。初めて読む486を読んだり、自作OS作成経験ある人には面白い章なんじゃないかと思います。

さて。早速遊んで見ようと思って気づいたことなどをめも。

まずは、必要な物としてエミュレータ(うちは遊べるような実機ないですし)、クロスコンパイラですね。
コンパイラgcc-arm-linux-gnuパッケージを入れます。yumで入れれば依存関係でbinutils-arm-linux-gnuパッケージも入るのでクロス開発環境としてはこれでOKです。あとはqemu-system-armパッケージを入れましょう。
これで環境はd( ^ω゜ )バッチリ!!と思ったら罠があります。

[masami@saga:~/codes/mikoto]$ yum info gcc-arm-linux-gnu
Loaded plugins: langpacks, refresh-packagekit
Installed Packages
Name        : gcc-arm-linux-gnu
Arch        : x86_64
Version     : 4.7.2
Release     : 2.aa.20121114svn.fc19.1
Size        : 28 M
Repo        : installed
From repo   : fedora
Summary     : Cross-build binary utilities for arm-linux-gnu
URL         : http://gcc.gnu.org
License     : GPLv3+ and GPLv3+ with exceptions and GPLv2+ with exceptions and LGPLv2+ and BSD
Description : Cross-build GNU C compiler.
            :
            : Only building kernels is currently supported.  Support for cross-building
            : user space programs is not currently provided as that would massively multiply
            : the number of packages.

Descriptionを読むとgccのクロスコンパイラパッケージはカーネルのビルドしかまだサポートしていないんですね。なので↓のコードもarm用に作ることができません。

#include <stdio.h>

int main(int argc, char **argv)
{
    printf("hello, world!\n");
    return 0;
}

すくなくとも2013/6/30時点でFedoraをホストにしてアプリをコンパイルしようと思ったら自前でツールチェイン等構築するか、エミュレータのarm環境が必要になります。
しかし、アプリはクロスビルドできなくてもカーネルはOKなので、一応遊べます。
俺のなかのマリー・ナントカネットさんが「アプリをクロスコンパイルできないならカーネルを書けば良いじゃない」と言っていましたw

ということで、単純なハローワールドを書いてみました。
書いたのは以下の4個です。

「ARMで学ぶ アセンブリ言語入門」の後半でirqやソフトウェア割り込みの扱いが載っていてそこで使用しているスクリプトを参考にしています。

では、まずはリンカースクリプトから。

ENTRY(mikoto_kernel_start)

SECTIONS
{
	. = 0x10000;
	.text : {
		*(.entry);
		*(.text, rodata);
	}

	.data : {
		*(.data);
	}

	.bss : {
		*(.bss);
	}

	.stack : {
		. = . + 0x1000;
		. = ALIGN(4);
		stack_top = .;
		. = . + 0x1000;
		. = ALIGN(4);
		irq_stack_top = .;
	}
}

bootで最初に呼ばれるのがmikoto_kernel_start、開始アドレスは0x10000で、スタックは0x1000からと言う感じです。
今回はirqは使って無いけど、一応記述は入れてあります。

次にboot時に呼ばれるアセンブラコード。

.text
.code 32

.globl 	mikoto_kernel_start

mikoto_kernel_start:
	ldr sp, =stack_top
	bl kernel_main

こちらはものすごく単純にして、スタックを設定したらc関数のkernel_main()を呼ぶだけです。
出力はシリアルポート(UART)です。
cのコードはこちら。

#define UART0 ((volatile unsigned int *) 0x101f1000)
#define UARTFR 0x06
#define UARTFR_TXFF 0x20

void 
puts(const char *s)
{
	while (*s) {
		while (*(UART0 + UARTFR) & UARTFR_TXFF)
			;

		*UART0 = *s;
		s++;
	}
}

void
kernel_main(void)
{
	puts("hello, world!");
	while (1);
}

最後にMakefileはこちら。

CC = arm-linux-gnu-gcc 
CFLAGS= -mcpu=arm926ej-s -marm -nostdlib --no-builtin -Wall

LD = arm-linux-gnu-ld
LDFLAGS = -T mikoto_kernel.ld

kernel = mikoto_kernel.img

objs = kernel_main.o \
	   head.o

all: $(objs)
	$(LD) $(LDFLAGS) $(objs) -o $(kernel) 

.c.o: 
	$(CC) -c $(CFLAGS) $< -c

.S.o:
	$(CC) $(CFLAGS) $< -c

clean:
	-rm *.o  *~

test:
	qemu-system-arm -M versatilepb -nographic -kernel $(kernel)

.PHONY: clean all

標準ライブラリは使わないので-nostdlibを付けるのと、gccがビルトインのputsが使おうとするのを防ぐため--no-builtinを付けてます。

そして、qemuで実行する場合はこんな感じのオプションで実行するとちゃんと動きます。

[masami@saga:~/codes/mikoto]$ qemu-system-arm -M versatilepb -nographic -kernel mikoto_kernel.img
pulseaudio: set_sink_input_volume() failed
pulseaudio: Reason: Invalid argument
pulseaudio: set_sink_input_mute() failed
pulseaudio: Reason: Invalid argument
hello, world!

こんな感じでFedora 19(x86_64)をホストにしてarmバイナリのコンパイル・実行ができました( ´∀`)bグッ!

FedoraのARMプロジェクトではVersatile Expressのイメージを配布しているのでこれで遊ぶこともできます。このイメージのディストリビューションはFedora18となっています。
イメージの取得、動かし方はFedoraProjectのWikiに書かれています。XFCEも動くようなのでGUIありで動かしてみたのが↓の写真です。
f:id:masami256:20130630205839p:plain