2009年12月21日

ARM Linux の移植

新しいターゲットボードに ARM Linux を移植するとき、何をすれば良いのか、各ファイルにはどのような役割があるのか、なかなかわかりにくいと思います。

自分の経験を元に、以下にまとめてみます。内容は無保証です。



ROM 化の部分は扱いません。make vmlinux できるところまでです。とりあえず vmlinux (圧縮前の ELF 形式) さえできれば、PARTNER から直接実行できます。
> l /path/to/vmlinux,/offs=xxxxxxxx  # ロードしたい DRAM 物理アドレス位置へのオフセットを指定
> _pc=xxxxxxxx # pc をロードした位置に合わせてセット。
> _r1=xxxx # machine number を正しくセットしておかないとブートできません。
> g # 実行

(それ以降は、compress して zImage を作るための定義などが、さらにいろいろ必要です。また、ブートローダーなども絡んできますので、ここでは割愛します。)

# ARM ボードには PC/AT のような標準的な構成が存在せず、ターゲットごとに本当に千差万別
# なので、ここで書いてることが必ずしも参考になるとは限らないことに注意してください。
# PC/AT の Linux の経験がある人向けに、NetWinder のように、古典的な PC/AT 構成の
# ボードを想定しています。(最近の複雑な ARM の SoC ボードに比べれば、PC/AT の
# レガシーデバイスが一番簡単なので。)

(1) arch/arm/tools/mach-types にターゲットボードの ID やプラットフォーム番号を記入します。

このファイルから、CONFIG_MACH_XXXXX や machine_is_xxx() などの定義が生成されます。

# machine_is_xxx CONFIG_XXXX MACH_TYPE_XXX machine_number
foo_board MACH_FOO_BOARD FOO_BOARD 12345678

とりあえず他のターゲットと被ってなければ、適当に決めて問題ありません。

(2) arch/arm/Kconfig にエントリを追加します。

ここらへんは、同じ arch の、似たような構成のボードを参考にすると良いです。私は versatile ボードを参考にしました。
config ARCH_FOO_BOARD
bool "foo sample board."
select ...
...
help
This enables support for foo board.

(3) arch/arm/Makefile にエントリを追加します。
...
machine-$(CONFIG_ARCH_FOO_BOARD) := foo_board
...

(4) arch/arm/mach-xxxx 以下を作ります。

ここがマシン依存部の本体で、一番大変なところです。

ターゲットに固有の定数やコード以外にも、マシン非依存部分から必要とされる様々な関数や定数の定義を、適切なファイルで適切に定義することが必要なので、最初は似たような構成のボードの mach-xxxx ディレクトリを丸ごとコピーして改造していく方が楽だと思います。

具体的には、arch/arm/mach-xxxx/include/mach/ 以下の、各種ヘッダとアセンブラマクロなどの定義ファイルです。

Porting Linux to a New ARM Platform なども参考になります。

その他に、ボードの初期化マクロ MACHINE_START などが定義された C ファイルと Makefile、そして Kconfig が最低限必要です。この中で、ドライバから使われる、そのボードでのデバイスのベースアドレスなども定義します。

以下、わかりにくそうなところに絞って解説します。

(5) IRQ の処理用のマクロを定義

arch/arm/mach-xxxx/include/mach/entry-macro.S の中で、

.macro disable_fiq
.macro arch_ret_to_user, tmp1, tmp2
.macro get_irqnr_preamble, base, tmp
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp

などのアセンブラマクロを定義します。これが arch/arm/kernel/entry-armv.S に include されて、その中の irq_handler マクロの中で使用されます。詳細は entry-armv.S の中を見てください。

versatile ボードでは、disable_fiq と arch_ret_to_user が空だったので、VIC や PIC などの割り込み要因レジスタのベースアドレスを base にロードする get_irqnr_preamble と、irqnr に IRQ 番号を拾ってくる get_irqnr_and_base マクロを定義すれば良いと思います。

__irq_svc と __irq_usr から irq_handler は呼ばれます。その中で、上記マクロで IRQ 番号を取得し、asm_do_IRQ がコールされ、割り込みハンドラが動きます。

(6) IRQ の初期化関数を定義

ここはターゲットの割り込みコントローラによって、定義するべきものが変わってきます。

一番簡単な、一般的な PIC (ARM は VIC が一般的なので、あまり ARM Linux では使われないと思いますが。) を例に挙げますと、struct irq_chip の ack/mask/unmask/eoi などの各メンバを、各種割り込みコントローラ操作ハンドラで初期化します。それを set_irq_chip() で IRQ 番号と結び付けます。

他に set_irq_chip_data()/set_irq_handler()/set_irq_flags() などの関数を使い、IRQ を定義します。PIC のように EOI の発行が必要な場合、handle_fasteoi_irq を set_irq_handler の引数に使用します。flags や chip_data などは定型文で問題無いと思うので、似た構成のボードを参考にしてみてください。

(7) メモリマップの初期化関数を定義

arch/arm/mach-xxxx/include/mach/vmalloc.h で、kernel が vmalloc() に使う空間のサイズを VMALLOC_END で定義します。

memory.h の中で、DRAM のオフセットを PHYS_OFFSET 定数で定義します。

初期化関数の中で、I/O 空間を struct map_desc と iotable_init() を使ってマッピングする際に、VMACLLOC_END 以降のアドレスでないとマッピングできない点に注意が必要です。

(8) タイマーを定義

struct sys_timer 構造体の .init メンバーを、タイマーの初期化関数で初期化します。

初期化関数の中では、clocksource_register() や clockevents_register_device() などを呼びます。

最低限、タイマーハンドラーがちゃんと動かないと、jiffies が増えないので、calibration delay loop から先に進まなくなってしまいます。

(9) デバイスドライバを登録

kernel に直接組み込むデバイスドライバを、platform_device_register() で登録します。

ここらへんは本当にデバイスごとに多種多様なので、同じデバイスを使っているターゲットを参考にすると良いです。

ioport を持つターゲットの場合は、mach/io.h での __io() マクロの定義が重要になってきます。
また、PCIMEM_BASE などの定数や __mem_pci() などのマクロもドライバの中から参照される場合があるので、適切に定義しておきます。

# 古典的な 16 色の VGA コントローラを使用した際、なぜか vga16fb ドライバ
# が PCIMEM_BASE 相対で VRAM アドレスをマッピングしていたのではまった記憶
# があります…

割り込みコントローラ、タイマー、シリアルコントローラが動けば、とりあえず Linux は動きます。

シリアルコントローラの初期化の仕方については、例えば struct plat_serial8250_port などで似たデバイスを持つターゲットのディレクトリを grep すればすぐわかると思います。他のデバイスも同様です。

(10) MACHINE_START() マクロを定義

.map_io
.init_irq
.timer
.init_machine

のそれぞれのメンバーを、今までに定義した構造体や関数で初期化します。
MACHINE_START(FOO_BOARD, "FOO Sample Board")
.map_io = fooboard__map_io,
.init_irq = fooboard__init_irq,
.timer = &fooboard_timer,
.init_machine = fooboard_init,
MACHINE_END

(11) 必要なドライバの Kconfig を見直す。

ドライバの Kconfig の、ドライバが使える条件 (make menuconfig の時に選択できる) のところに、ターゲットの名前がハードコードされている場合があります。

depends on .... && (ARCH_XXXX || ARCH_YYYY)

これはもうどうしようも無いので、そのドライバが必要な場合は、自分のターゲットの ARCH_XXXX を追加しましょう。

また、そのようなあまり汎用的では無い作りのドライバのソースコードの中では、さらに #ifdef がハードコードされている場合があるので注意が必要です。

(12) make menuconfig

通常の Linux kernel のビルドと同様に設定します。

(13) make vmlinux

vmlinux ができればとりあえずビルドは成功ですが、一発でちゃんと動くことはあまり無いと思うので、トライアンドエラーで頑張りましょう。

弊社の PARTNER がデバッグの手助けとなれれば幸いです。

トラックバックURL

コメントする

名前
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード