(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

前回はシリアル2本を使えるようにしたので,今回は調子に乗ってGDB対応してみよう.いわゆるリモートデバッグというやつだ.ちなみにGDB対応については第10回で説明しているが,GDBスタブの実装が必要になる.

で,GDBスタブなのだけど,第10回ではgdbのソースに付属しているi386用のスタブを改造して利用したのだけど,残念ながらPowerPC用のスタブは付属していないのだな.なのでPowerPC用のスタブ作成が必要になる.

ここでスタブの実装方法についてなのだけど,実装方法には2通りある.
  1. 不正メモリアクセスなどの例外処理を直接ラッチしてスタブに渡す.
  2. 不正メモリアクセスの発生時にはOSの例外処理に入り,その中からスタブの処理を呼ぶ.
で,本来ならば1の実装をすべき.というのは2の実装だとOSを通してしまうので,OSのデバッグに利用できないからだ.

ただ1の実装だと,OSが設定している割り込みハンドラを書き換えて,スタブ側の割り込みハンドラが呼ばれるようにする必要がある.これは通常は set_debug_traps() という関数を用意して,set_debug_traps() が呼ばれたら割り込みハンドラをごっそり置き換える(そしてデバッグの有効化時には,set_debug_traps() を呼ぶ)という処理を行うようにする.

あと当然だがスタブ側で割り込みハンドラを用意する必要が出てくる.これはアセンブラで書く必要がある.スタブのサンプルを見ると,インラインアセンブラでなにやら書いている部分が多くあるが,これが独自割り込みハンドラのコードだ.

で,本来はこういったものをPowerPC用に用意しなければならないのだけど,今回は面倒なのでやらないで上記2の実装にすることにする.つまり割り込みのハンドリングはOSに任せて,OSからスタブを呼んでもらうようにする.このようにすることで,割り込みハンドラの準備が不要になり,スタブの実装はかなり楽になる.

ついでにいうと,現状ではKOZOSのPowerPC版は,割り込み発生時にはOS用のスタックを0x200000という決め打ちの値で取っている(これは startup.s の _intr 参照)ので,もしもスタブ側で割り込みハンドリングするとしたら,これとはぶつからないようなアドレスに別途スタックを確保する必要がある.まあこれは別の話なので今は考えなくていい.

で,上記2の実装を採用することで,アセンブラを書く必要が無くなるので,スタブの実装はすげー楽になる.実際にはゼロから作るのではなくてきとうなCPU用のスタブを移植するのが楽だが,移植時には具体的には以下の点だけ修正すればよい.
  • レジスタの保存方法
  • 割り込みベクタ番号からシグナル番号への変換方法
  • ステップ実行時のレジスタ設定
  • ブレークポイントでのトラップ命令実行
まあこのへんは実際にソースコードを紹介しながらあとで説明しよう.

で,実際にGDB対応したのが以下のソースコード.スタブはいちおう最新である第42回のi386-stub.cを流用して ppc-stub.c というのを作成した.差分については diff をとるなどして確認してほしいのだが,とりあえず修正点の説明は次回あたりにまわそう.GDB対応については説明することがいろいろあるので,今回はひとまず動かしてみることにする.(だって動いているのが見たいよね)

で,ソースコードの修正はこれでいいのだけれど,まずはPowerPC用のGDBを用意しなければならない.で,GNUのサイトからてきとうにダウンロードする.とりあえず現在はgdb-6.8が最新のようなので,それを持ってきた.

ぼくの環境はFreeBSDなのだけど,まずは解凍して,

% tar xvzf gdb-6.8.tar.gz
% cd gdb-6.8
% ./configure --target=powerpc-linux --prefix=/usr/local --disable-nls
...(中略)...
% gmake
...(後略)...

で,うまくビルドできた.注意として ./configure 時に --disable-nls が無いと,

sysdep.h:173:21: libintl.h: No such file or directory

というようなエラーで途中で止まってしまう.あとビルドは gmake で行う必要があるみたい.BSD make を利用すると,

config.status: creating po/POTFILES
config.status: creating po/Makefile
"Makefile", line 1146: Need an operator
make: fatal errors encountered -- cannot continue
*** Error code 1

とかなって,やっぱし止まってしまう.

ビルドが終了すると,gdb-6.8/gdb というディレクトリ以下に gdb という実行形式が作成される.これが PowerPC 用のGDBの実行形式になる.

では実際に動かしてみよう.まずはcuで

# cu -l /dev/cuad0 -s 115200

のようにして,ターゲットボードにシリアルで接続する.で,ファームウエアをいつもどおりターゲットボードにダウンロードして起動.

画像はこちら

ここでtrapコマンドを実行してみよう.

画像はこちら

なんか「$T051:0030bf00;:000423ac;thread:0005a7a4;#8a」という,わけのわからんメッセージが出てきた.これは実はターゲットボードのGDBスタブが動作して出しているメッセージだ.trapコマンドを実行することでPowerPCのトラップ命令が実行され,スタブに処理が渡ったのだ.で,スタブはPC上のGDBに対して,トラップ命令でブレークしたことを通知しようとしてメッセージを出力しているわけだ.

このターゲットボードはシリアルを2本持っていて,本来は1本目を通常用途,2本目をGDB用に使おうかなーと思っていたのだけど,シリアルケーブルが手元に1本しかないので,とりあえず今回は通常用途とGDB用で共有するようにしてある(PSC1のみ利用し,PSC2は利用していない).なのでtrapコマンドを実行したコンソールに,GDBメッセージがそのまま出てきてしまっている.

で,GDBと繋ぐためにはとりあえずコンソールは終了しなければならない.これはcuの場合には,「~」「.」を連続して押下することで抜けられる.

画像はこちら

次に,emacsを起動する.まあ実はgdbはemacs無しで直接起動することもできるのだけど,emacsから利用したほうが絶っっっ対に便利なので,とりあえずemacsを起動してしまう.

画像はこちら

ここで M-x gdb で,gdbモードに入る.M-x ってなんだかわからないひともいるかもしれないが,これは[ESC][x][g][d][b]と順番にキーを押せばよい.

画像はこちら

起動するgdbがデフォルトのままでいいか聞かれている.これはこのままだと,FreeBSDに標準で付属しているi386用のgdbが起動してしまう.今回はPowerPC用にビルドしたものを利用しなければならないので,BackSpaceで削除して,ビルドしたgdbを指定する.あと実行形式には,ファームウエアのビルド時に作成されたELFファイルである「sample」を指定する.

画像はこちら

Enterを押すと,gdbがスタートする.

画像はこちら

ビルドしたgdbが起動していることの確認として,gdbのバージョンが6.8になっていることを一応確認しよう.

ここでシリアルの速度を115200bpsにするために,

(gdb) set remotebaud 115200

を実行する(デフォルトでは9600bpsになっている).さらに,

(gdb) target remote /dev/cuad0

を実行することで,シリアル経由でターゲットボードに接続する.

画像はこちら

おお!接続できて,ブレーク箇所が表示された!

今回利用したスタブは,第23回でスレッド対応済みのものを流用したものだ.なのですでにスレッド対応されている.ためしに info threads を実行して,スレッド情報を見てみよう.

画像はこちら

おー,ちゃんと表示されているみたい.すげー.

ちなみにもしもうまく接続できなかったら,chmod 666 /dev/cuad0 する必要があるかもしれない(デフォルトでは /dev/cuadX は660になっているので).あと,PC上のGDBとターゲットボードとの間の通信の内容とかは,

(gdb) set debug remote 1
(gdb) set debug serial 1
(gdb) set debug target 1

を設定しておくことで参照できる.これはスタブのデバッグ時に役に立つ.

うーん,1日であっさり動いてしまった.まあいろいろ詰めておきたいところは残っているのだけどとりあえずよかったかな.ていうかちょっと感動.

あ,ソースコードの修正内容の説明をまったくしていなかったね.これは次回!
(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

Interfaceの記事とかがいろいろと忙しくて更新が滞っていたのだけど,PowerPCへのGDBスタブ移植を見越して,シリアル周りをちょっと拡張してみた.

移植編第1回では,シリアルは1本のみの利用だったのだけど,このターゲットボードは写真を見ればわかるように,シリアルインタフェースを2本持っている.

なので,シリアルを2本とも使えるように拡張してみた.移植編第1回からの修正点は毎度どおり diff.txt に差分を取ってあるので,細かい修正内容はそちらを参照してほしい.

まず,今回利用しているPowerPCオプションCPUカードにはシリアルインタフェースが2本あるのだけど,これらはCPU(MPC5200)のPSC1,PSC2というコントローラが接続されている.で,従来は serial.c ではPSC1のみ決め打ちで利用するようになっていたのだけど,各関数が index という引数を取って,PSC1とPSC2を選択して動作するようにserial.c を修正した.

さらに extintr.c ではシリアル2本を管理するように修正.あとコマンドスレッドをPSC1,PSC2用に2本起動して,index を切替えて動作するように command.c を修正.あと command.c にはシリアルの index を表示するための,indexコマンドというのを新たに追加してある.

あとついでに startup.s でコードがまとめられる部分があったのでまとめてみた.あとタイマ関連で不要な宣言とかが残っていたのをごっそり掃除.

では動かしてみよう.ボードに電源入れてファームウエアをインストールして起動する.

# bootm 80000
## Booting image at 00080000 ...
Image Name: smplimg
Created: 2009-04-06 12:19:03 UTC
Image Type: PowerPC U-Boot Standalone Program (uncompressed)
Data Size: 16744 Bytes = 16.4 kB
Load Address: 00040000
Entry Point: 00040000
OK
puts ok
printf ok
PVR = 80822014
MSR = 0000b002
MSR = 00003002
MBAR (from SPR) = f0000000
MBAR (from MBAR) = 0000f000
MBAR (from bd) = f0000000
IBPclock = 66000000
baudrate = 115200
MSR = 00003002
MSR_EE set OK
>
OK
> echo aaa
aaa
OK
> index
0
OK
> ★★★ここでシリアルをPSC2側に挿し替え
OK
> index
1
OK
> threads
extintr
idle
command0
command1
OK
> index ★★★ここでシリアルをPSC1側に挿し戻す
0
OK
>

あっさり一発で動いた.とくに問題無し.

シリアルを抜き挿ししてコマンド実行.index コマンドで,はじめは0と出ているが挿し替え後は1と出て,再度挿し戻した後は再び0と出ているのに注目.

これでシリアルが2本とも利用できるようになった.で,次はgdbスタブを移植して,シリアルの片方はgdb用に利用するようにするつもり.

しばらくInterfaceの記事とかのほうで忙しくKOZOSの移植作業が進んでいないのだけど、移植先をいろいろと探してはいる。


で、こんなもの を知り合いに教えてもらった。coregaのボードコンピュータで、もちろんLinuxが移植済み。


問題のCPUは、ADM5120というよく知らない石が乗っている。どうもMIPSらしい。


値段も1万円と手ごろなので、とりあえず注文してみた。時間ができ次第、移植を進めてみよう。


ルータとしての動作を意識しているのか、LANが2ポートあるのでいろいろ楽しめそうだ。(KOZOSで簡易ルーティングなんてことも少し考えている)



(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

えーと,移植編第1回であえて書かなかったことなのだが,実は一番言いたかったことがある.まずは移植編第1回のソースコードを見てほしい.

% ls
Makefile crtn.c kozos.h make.sh startup.s
command.c extintr.c ld.scr memory.c syscall.c
configure.h extintr.h lib.c memory.h syscall.h
crt1.c idle.c lib.h serial.c thread.c
crti.c kozos.c main.c serial.h thread.h
% cat *.[ch] | wc -l
1933
% cat thread.c | wc -l
554
%

ソースコード全体で,2000行を切っている.中核である thread.c にいたってはわずか500行程度.これだけでも,スレッド管理やメモリ管理をしてくれて,スレッド間通信も提供されている.OSってこれくらいで作れちゃうもんなんだな.

どうも,OS作るのは非常に難しいと無駄に思われている気がする.しかしOSって簡単なものならば作ることはたいして難しいことではなく,ソースコードの量的にはむしろパッと作ってしまえるようなものだ(実際,KOZOSの原型は1日程度で作っている).そりゃまあファイルシステム積んだりネットワーク機能積んだり多CPU対応したり様々なデバイスに対応したり汎用的な構造にしたりすれば,そりゃもうたいへんな時間と知識が必要だ.だけどそれらはOSの本質ではなくて,最低限の機能に抑えれば,わずか2000行程度で書けてしまうもんなのだ.

組み込みOSはとっつきにくいとか勉強が難しいとか自作はたいへんと思われている理由なのだけど,思うにはじめるまでの敷居が高いのが原因だと思うのだな.まずは動かしてみてみたいので,なんらかの組み込みOSが動作するターゲットボードを選ばなければならないのだけど,これが数万円もして,それでも頑張って購入して,ビルド環境作りに3日間,OSがビルドできるようになるまでさらに3日間,で,ようやくファームウエアが作成できて,インストールしてリセットボタン押したらウンともスンとも言わない...こんな感じで,普通はモチベーションが続かずに終ってしまう.ほんとはOSの勉強がしたいのに,それを動かして試してみるというスタートラインに立つまでだけで,いろんなハードルがあるわけだ.これは,それが本業ならばいいのだけれど,勉強のために個人でやるとか,中学生や高校生が遊びや部活動でやるとなると,作業的にも金銭的にも,ちょっと厳しいだろう.KOZOSのユーザーランド動作は,このような現状に対するひとつの回答でもある.

世の中にOSは星の数ほどあり,オープンソースなOSもいろいろあるのだけど,OSの勉強となると,サンプルに適切なOSがあまり無いと思う.まず思い浮かぶのはLinuxやNetBSDだけど,これらはOSの勉強には大きすぎるし,まず全貌を理解できない.

あとはμiTRON系のTOPPERSHOSなのだけど,これらも(OSの基本構造の学習用サンプルとしては)大きすぎると思う.あとμiTRON系だと,結局はμiTRONの勉強になってしまい,「OSを作る」という学習には向いていないと思うのだな.というのは,μiTRONのかなり完成された仕様がすでに存在しているので,自分でシステムコールを設計したり追加したりする,という学習に向いていないと思うのです.仕様がはじめから用意されているので,どんなシステムコールが必要なのか?そのシステムコールがなぜ必要なのか?という学習にも向いていない(そのシステムコールははじめからありき,という雰囲気になってしまう).あとわからないことあるとすぐに「μiTRONの仕様を読んで...」ということになってしまい,これってOSの勉強でなくμiTRONの(仕様の)勉強だよなあ,ということになりがちだ.

実際,μiTRON関連の書籍とかはいっぱいあるのだけど,「μiTRONの上でアプリを作って動かしてみよう」「μiTRONを使って制御実験をしよう」というような感じで,まずμiTRON系OSありきで,その上でアプリを動かす,という内容のものがほとんどであり,OSを自分で設計して作る,どのようにしたら便利な(実用的な)OSになるか考える,という方向性のものではない.

で,KOZOSの目指すところとなる.
  • PC-UNIX上で動作することで,誰でも簡単に(ボードを購入することもなく)試せる.
  • さらに実ハードウエアで動かしたいと思っても,数千円で買えるような(中学生がおこずかいで買ったり,高校のパソコン部が部費で買えるような)ボードの上で動作する.
  • ソースコードは余計なものは一切はぶいて,要点のみわかりやすくする.大規模開発やオブジェクト指向などの知識が無くても読める.プロのプログラマとしての知識が無いアマチュアにも読める.
  • 多CPU対応については,ソースコードは共通化せずに各CPUごとに専用化して,読みやすくする.
  • 多ボード対応時に,そのボードでのスタートアップガイド(まあ,このホームページですが)を充実させる.
  • ビルドのためにスクリプト実行とかは不要.動くソースコードをパッと読める.
このへんがKOZOSの目指すべきところなのではないかと思うのだな.実用性とか,多デバイス対応とか,多CPU対応とか,そのときのソースコード共通化とかは考えないわけだ.だってそのようなきちんとしたOSはすでにいっぱいあるし.まあもともとKOZOSはぼくの趣味と勉強のために作っているのだけれど,せっかく作るのならば,今までに無いものにしたいなあというわけだ.
(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

今回から,新章として「KOZOSの移植」という別テーマで並行して連載をします.まあ実ボードへの移植ということで,いろんなボードコンピュータにKOZOSを移植して,そのいきさつなどを書いてみようかな,と.

何度も書いているがKOZOSはPC-UNIX上で動作するユーザーランドOSであり,FreeBSDやLinuxの上でパッと試せるのがウリなわけだ.ただそれだけだと単なるスレッドライブラリと変わらないので,内部は組み込みOSの作りを模擬している...ということなのだけど,やっぱし実際に実ハードウエアの上で動いているものが無いと,信憑性がないというか,所詮机上の話でしょ?みたいな感じがしちゃうよね.あとはやっぱし実ハードで動いているのを見たい!という思いもある.まあオープンソースカンファレンスに出展して,やっぱし実ハードの上で動いてないとインパクト無いなーと思ったというのが大きな理由ではある.

で,実ハードへの移植なのだけど,「実ハード」といってもピンとこないひとたちのために,ちょっと説明.

OS自作というと,これはLinuxとか30日でできる! OS自作入門の影響が大きいのだと思うのだけど,AT互換機(Windowsが動く,いわゆるPC)で動作するOSを思い浮かべる人が多いと思う.

しかしKOZOSはこーいうようなOSを想定しているのではなくて,組み込み機器上で動作する,組み込みOSを想定している.組み込み機器というのは,たとえばPDAとか携帯電話とか,FAXとかプリンタとかカーナビとか炊飯器とか,そーいうもののことだ.これらは高性能/低性能の差はあれど,いずれもCPUと呼ばれるものが入っていて,動作している.まあ炊飯器くらいだと,そこまで大げさには言わずに「内部にマイコンが入っていて,制御を行っている」などと言ったりもするが,基本的なところは同じで,要するにCPUがプログラムを実行して動作している,ということだ.で,このプログラムのバイナリのことを,組み込み機器の場合は一般に「ファームウエア」と呼ぶ.

これは単純な制御だけならOS無しで1本のプログラムが動くだけ,という構成でも作れるのだが,組み込み機器と言えど最近は高機能のものが多く,様々なサービスを行っていたりする.こーなるとやっぱしOSが必要になってくる.複数のサービスを動作させたい場合には,OSを使うことでそれぞれのサービス用のプログラムを独立して書くことができるようになるからだ(この詳細についてはまたそのうち書こう).たとえば携帯電話ならば,時計としての機能や着信待ち受け,目覚し時計,スケジュール管理とその通知,ボタン押し下への反応など,さまざまなサービスが常時動作している(はずだ).これらの機能をそれぞれ順次切替えながら動作させる1本のプログラムなど,あまり書きたいものではない.ということで,OSの出番となる.このために使われるOSが,組み込みOSだ.

これに対してPC上で動作するWindowsとかLinuxなどのOSは,汎用OSと呼ばれる.汎用OSと組み込みOSでは求められるものが全然違うので,まあ自作OSというとつきものの「仮想メモリが無いと実用にならない!」とか「ファイルシステムが無いなんてOSとは言えない!」とか「ソケットが無いとネットワークプログラムが書けないではないか!」とか言ったような話(そんなに言うなら自分で作りゃいいと思うのだが)は,そのまま当てはまらない.まあOSの本来の目的は資源管理にあるので,CPU時間の管理としてスレッドの実現,スレッド間通信の方法の提供,あとメモリ資源の管理くらいしてくれれば,十分にOSと言っていいとは思う.

で,KOZOSなのだけど,KOZOSはこのような組み込みOSを模擬してPC-UNIX上で動作する.つまり本来の意味での組み込みOSではなく,組み込みOSの勉強をするために,PC-UNIX上で動作するOSシミュレータのようなものだ.ということで実際に組み込み機器に移植して動作させたい,というのが今回のテーマだ.

で,移植先の組み込み機器なのだけど,実際に携帯電話とかPDAとかに移植するのはまああまり現実的でない.というのはこのような機器はファームウエアのビルド方法などはまあまず非公開(それどころか,むしろ機密)だし,アマチュアレベルで手を出すのは(著作権とかそーいう法律的な問題も含めて)かなり敷居が高い.ていうかそれをやりだすと,OSの勉強ではなく単にその機器の勉強になってしまい本末転倒.ということで,移植先のターゲットには以下の候補がある.
  1. 評価用のCPUボード
  2. 製品版のCPUボード
  3. Linuxが動く小型コンピュータで,自分でLinuxインストールできるような製品
  4. 安価なCPU基板
まず1の「評価用のCPUボード」だけど,まあたいていのCPUでは,そのCPUを導入する前にお試しで使ってみるための評価ボード(evaluation board)というものが各社から出ている(CPUベンダが出している製品もあるし,代理店など他の企業が出している製品もある).これはまずそのCPUがど真ん中に置いてあって,そのCPUに接続できるI/Oがひととおり(シリアル,ethernet,USB,VGAなど)取り付けられているというものだ.「リファレンスボード」といったりもする.たとえばこれとかこれとかだ.「評価ボード」で検索すると,いっぱい出てくる.

これは実は,移植先として理想的(開発環境など充実しているし,I/Oもいっぱいついているし,作りは汎用的だし,移植のためのものなので内部資料なども充実)なのだが,大きな問題がある.というのは,なんといっても高価なのだ.まあ金額は見てもらえばわかると思うのだが,平気で10万とかしたりする.企業が自社製品のCPU選定のために評価用に購入するならばたいして高価とはいえないのだけれど(実際,10万くらいで「安価」をうたっている製品も見かけたことがある気がする.まあ,たしかに安価ではある),これではアマチュアレベルで勉強や遊びのために買っていじってみたいというのは,まず無理だ.ましてやOSいじって遊んでみるために,中学生がおとしだまで買ったり,高校のコンピュータ部が部費で買ったりできるようなものではない.しかし,KOZOSはそーいうためのサンプルOSにしたいという思いがあるので,これは却下になってしまうのだな.

次に製品版のCPUボードだけど,これはCPUの評価が目的というよりも,あるCPUと周辺I/Oがコンパクトな基板上にまとめられていて,それをそのまま流用して小型サーバとかのような組み込み機器を作れる,というものだ.これは,たとえば小型サーバ製品を作りたくてソフトウエア技術はあるのだけど,ハードウエアを作る技術が無いような企業が,基板は他の会社から買って,そこに自社製のソフトウエアを乗せて自社製品として売る,というような用途に使うものだ.たとえばSilicon Linux(ここの社長の海老原さんには何度か面識がある)のCAT709とか,アットマークテクノ朱雀(これは実はFPGAボードなので,CPUボードというわけではないけど)とかだ.ちなみに評価ボードとか製品版ボードも含め,CPUが乗っていてとりあえずコンピュータとして動作する基板のことを一般に「ボードコンピュータ」と言う.

で,これも移植先としてはけっこう理想的.I/Oは限定されていたりするが,評価ボードよりも安価だったりするし,たいていはその目的のため,汎用的に作ってあるからだ.あとたいていはLinuxとかがすでに移植済みで,とりあえず買ってLinux動かして遊んだりも手軽にできる.あと基本的には製品にそのまま組み込むボードなので,それなりの信頼性が確保されていたりする(高価な電解コンデンサが使ってあるので壊れにくい,とか,きちんとした出荷検査が行われている,とか).

でもいくら評価ボードよりは安価とはいえ,やはり数万はするので,ギリギリ買える金額だとは思うのだが,遊び用途で買うにはまだまだ高価だ.あとは基本的には製品にそのまま組み込むボードなので,営業さんに「いくらくらいですか?」と聞くと,「数が出るならばお安くできます」と返されたりする.で,この「数が出れば」というのは,数千単位(数万単位?)で買ってくれるならば,という意味だ.要するに,個人への単品売りというのはあまり考えられていなかったりする(CAT709はホビー用途なら割引で買える(すばらしい!)のだが,それでもやっぱし数万してしまう).なのでこれも候補としては有力なのだけど,とりあえずは保留かな.

で,次に,「Linuxが動く小型コンピュータで,自分でLinuxインストールできるような製品」というやつだ.本来は小型サーバとかに使うのだけど,動作はLinuxで,しかも自分で作ったカーネルをインストールもできるので,好きに使っていいよ(もちろんその場合は保証やサポートは無いが)というものだ.こーいうのはあるようでなかなか無いものなのだけど,有名なところではぷらっとホームOpenBlockSとか,玄人志向玄箱とかがある.

これらのいいところは,まずきれいなケースに入っていて(ボードコンピュータはたいてい基板むき出し),あとはOSの勉強に飽きたら本来のLinuxに戻して,それはそれでサーバとかにして遊んでみることができるということだ.ただ,基本的には添付のLinuxを入れて動かすものであり,自分でOSをインストールして動かすというのは,たぶんできるとはいえ未サポートなので,そのための仕組み(ファームウエアインストール方法やブート方法など)やドキュメントがあまり整備されていない場合が多い(でも,そーいうコミュニティとかが充実していたりすることは多い).あとこれらもやはり数万はするので,まだまだ高価.まあでも飽きたらサーバとしても使えることを考えると,それなりに買いやすいものだとは思う.

で,最後に「安価なCPU基板」というやつなのだけど,これが実は本命になる.これはたとえば秋月組み立てキットとか,毎年春になるとInterface誌に付録でついてくるCPU基板とかだ.

これらの利点は,とにかく安いこと.せいぜい数千円のレベルだ.ただし安いぶんデメリットももちろんあって,まずは使えるI/Oがたいてい少ない(シリアルだけとか),あとはコネクタとかが最初からついていなくて,自分ではんだづけしてつける必要があったりする.基本的にはCPUのGPIOの線が引き出してあるので,コネクタをはんだづけしてGPIOで信号を入出力して,センサとA/DつけてLED光らせたり液晶パネルに文字を出して遊ぶ,ということになる.あとファームウエアをインストールするのも,ROMライタで直接焼き込み,みたいなものも多い.

でもやっぱし安いことはアマチュアにとっては最強の利点だ.ということで,まずはこれらの安いボードをターゲットにして移植を進めていきたい,と思う.

あと,はりぼてOSのように,PC上でフロッピーブートという方法も対応したいとは思う.なんだかんだいって,PCが一番入手しやすいハードウエアだからだ.

で,記念すべき第1回の移植先ターゲットボードはこれ.

画像はこちら

ここまで延々とターゲットボードの選択基準について書いてきて,いきなり路線と全然違うものになってしまってゴメン.これはCQ出版組み込みシステム開発評価キット(通称BLANCA)用の,PowerPCオプションCPUカードだ.まず「組み込みシステム開発評価キット」というのは,FPGAを2個積んでいて,各種コネクタがFPGAに繋がれているので,FPGAで好きに各種コントローラを作って遊んでみましょう,というものだ.で,これにオプションとしてCPUボードを積むことができて,そのPowerPC版のボードが今回のターゲットとなる.

画像はこちら

こっちの画像はBLANCAに積んだ状態.まあこのボード自体は単体でも動作するので,必ずしもBLANCAが必要なわけではない.

モノ自体はタンバックMPC5200DIMM(TB0286)というボードで,このボードにはメディアラボu-bootを移植している(このへんのことはInterfaceの2009/02号に詳しく書いてあるので,そっちも見てほしい).

なんでこのボードなのかというと,まあ実はこのボードを使っていろいろ動かしてみようという連載をいまぼくがInterfaceで持っているので,手元にサンプルがあるのでとりあえず手軽だということだ.ボード自体はやはり5万円くらいするので個人で買うにはちょっと高価だが,このボードはu-bootが動作するので,自作OSをインストールして動かしてみるには技術的な敷居がけっこう低く,(金額を別にすれば)OS自作にはおすすめなボードだ.あとはPowerPCへの(ていうかこのボードへの)移植は実は非公開だったのだけどずいぶん前にすでに行っていて,すぐに対応できた,というのも理由にある.

ということで実ハードウエアへの移植なのだけど,ほんとうはこういうのはユーザーランドOSとしてのソースコードと実ハードウエア上の動作のためのソースコードは共通化して,1本のソースコードからそれぞれビルドできるようにすべきだ.で,はじめはそーいうふうに書いていたのだけど,現状のKOZOSのソースコードはユーザーランドOSとしての対応部分がけっこう多く,#ifdef で分けていってもなんだかごちゃごちゃした感じになってしまうのだな.

もともとKOZOSはパッと読むことができるサンプルOSというものを目指しているので,こーいう理由でKOZOSのソースコードが読みにくくなるというのはぼくはちょっと避けたい.というのは,OSとしてのソースコードが,OSとしての機能のために複雑になってしまうのならば,それはそれでしょうがない.OS作ったらそうなる,ということだからね.だけど多機種対応などのような,OSの本来の機能とはべつのことでソースコードが複雑になってしまっていると,OSとして結局は何がやりたいのかが見えにくくなってしまうと思う.もしもOSを勉強しようとしたときに,これはきっとすごくじゃまなことだ.

同じような理由で,ビルド前に configure スクリプトを走らせるとビルド用のソースツリーができて,ビルドはその後に行う,というような構成も避けたい.これも,ソースコード読んで勉強しようとするときに,すごくじゃまだからだ.(OSの勉強でソースコードが読みたいのに,その前にスクリプト走らせたり(そしてうまく走らせられなくてハマったり),スクリプトの内容を読んだりしなければならなかったりする)

ということで多CPUへの移植は,第42回のソースコードから不要部分をごっそりそぎおとして簡略化したものをベースにして作業を行おうかと思う.つまり本家のソースコードとは分かれて,移植用のソースを別立てするということだ.本家のほうではそのままリアルタイム性の追求とかOS機能の拡張とかのお試しを引続き行う.

移植時には,移植先のボード単位でソースコードは別立てする.これも,もちろん本来は1本のソースコードから多ボードのファームがビルドできるようにすべきだ.そうしておけば,そのソースコードに修正を入れれば,すべてのボードに修正が反映されるからだ.でもKOZOSでは,これはあえてしない.そのボード専用のソースコードにしてしまって,そのボード単体での読みやすさ,ビルドしやすさを優先する.(機能を抽象化してソースコードを共通化・整理するのは,OSの構造を理解した後に,個別に行えばよい)

なぜこのようにしたいのかというと,ぼく自身がμiTRONの勉強(というより,組み込みOSの勉強)をしようと思ってTOPPERSをいじろうとしたときに,コンフィギュレータの動作がよくわからずに中途半端に挫折した,という経験があるからなのだ.こういうのって,まあ本人の勉強不足と言ってしまえばそれまでなのだが,本来はOSの勉強がしたいのに,それ以外の部分(OSの機能とは直接関係の無い部分)でハマってしまって挫折するというのは,非常にもったいないことだと思うのだ.

で,移植してみたのがこれ.

最初のサンプルとしては,シリアルを使ってコマンド応答をするだけの簡単なものを作ってみた.前述したように第42回のソースコードから不要部分をごっそり削って,PowerPC用コードを入れてある.PowerPC依存部については,Interface2009/02号からの連載「実践的PowerPC活用テクニック」で使っているソースコード(スタートアップとか,割り込み処理とか,シリアル制御とか)を参考にしている.(これは,ぼくが書いている記事です)

ちなみにPowerPC用のスタートアップとしてstartup.sが追加されているが,PowerPCアセンブラに関してはInterface2006/02号特集記事の「PowerPCアセンブラのエッセンス」を参考にしてほしい(これも,ぼくが書いた記事です).PowerPCのアセンブラ関連の書籍とかはあまり見かけないのだけど,この記事を読めばとりあえず簡単なアセンブラプログラミングはできる,というように書いてあります(ぼく自身,この記事はアセンブラ初心者をかなり意識して書いている).

注意点としては,まずスレッドのディスパッチ部分がアセンブラで書いてある.これは startup.s の以下の部分.

.globl dispatch
.type dispatch,@function
dispatch:
mr 1,3

lwz 2,128(1)
mtlr 2
lwz 2,132(1)
mtcr 2
lwz 2,136(1)
mtctr 2
lwz 2,140(1)
mtxer 2
lwz 2,144(1)
mtsrr0 2
lwz 2,148(1)
andi. 2,2,0xffff
mtsrr1 2

lmw 2,8(1)
lwz 0,4(1)
lwz 1,0(1)

sync
isync
rfi

スレッドのコンテキスト保存領域(kz_thread 構造体の context メンバ)から各レジスタに値を戻して,rfiで復帰するようになっている.dispatch は thread.c の thread_intrvec() の終端で,以下のようにして呼ばれている.

dispatch(&current->context);

ここは従来は setcontext() でコンテキストスイッチしていたところなのだが,startup.s の dispatch を呼ぶように修正している.

さらに割り込み発生時には,startup.s の _intr というハンドラが呼ばれ(これは main.c:sethandler() で割り込みハンドラ領域に登録している),レジスタの値をカーネルスタック(0x200000で決めうち)に退避してからdefhandler() というC言語の関数に処理を渡すようになっている.これは startup.s の以下の部分.

_intr:
mtsprg2 1

lis 1,0x200000@h
ori 1,1,0x200000@l
stwu 1,-160(1)

stmw 2,8(1)
stw 0,4(1)
mfsprg2 2
stw 2,0(1)

mflr 2
stw 2,128(1)
mfcr 2
stw 2,132(1)
mfctr 2
stw 2,136(1)
mfxer 2
stw 2,140(1)
mfsrr0 4
stw 4,144(1)
mfsrr1 5
stw 5,148(1)

bl 1f
1: mflr 3

lis 2,defhandler@h
ori 2,2,defhandler@l
mtlr 2
blrl

lwz 2,128(1)
mtlr 2
lwz 2,132(1)
mtcr 2
lwz 2,136(1)
mtctr 2
lwz 2,140(1)
mtxer 2
lwz 2,144(1)
mtsrr0 2
lwz 2,148(1)
andi. 2,2,0xffff
mtsrr1 2

lmw 2,8(1)
lwz 0,4(1)
lwz 1,0(1)

sync
isync
rfi

またシステムコールは従来はSIGSYSシグナル発行だったが,PowerPCのsc命令実行に置き換えられている.これは syscall.c の以下の部分.

void kz_syscall(kz_syscall_type_t type, kz_syscall_param_t *param)
{
current->syscall.type = type;
current->syscall.param = param;
asm volatile ("sc");
return;
}

あとは,extintr.c の割り込み処理部分で,serial.c が持っているシリアル関連のライブラリ関数を呼び出すように修正してある.あと他にもすっきりさせるためにこまかいところをいろいろと削ってあるのだが,まあいちいち説明はしない.

ではファームウエアをビルドして,インストールし,起動してみよう.まずはビルド環境の構築なのだけど,実はシリアルなどの動作のためにu-bootのヘッダファイルを参照しているので,u-bootのビルドが必要.まあビルド方法に関しては,実はInterface2009/02号の記事と同様なので,そっちを参照してほしい.

hiroaki@teapot:~/tb0286/kozos>% ./make.sh
+ LOCAL=/usr/local
+ TARGET=powerpc-linux
+ TARGETDIR=/usr/local/powerpc-linux
+ PATH=/usr/local/powerpc-linux/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/usr/pkg/sbin:/usr/pkg/bin:/usr/X11R6/sbin:/usr/X11R6/bin:/home/hiroaki/bin: export PATH
+ GCC_EXEC_PREFIX=/usr/local/powerpc-linux/bin export GCC_EXEC_PREFIX
+ AR=/usr/local/powerpc-linux/bin/ar export AR
+ AS=/usr/local/powerpc-linux/bin/as export AS
+ CC=/usr/local/powerpc-linux/bin/gcc export CC
+ /usr/local/powerpc-linux/bin/gcc -print-prog-name=cpp
+ CPP=cpp export CPP
+ CXX=/usr/local/powerpc-linux/bin/gcc export CXX
+ FC=/usr/local/powerpc-linux/bin/f77 export FC
+ LD=/usr/local/powerpc-linux/bin/ld export LD
+ NM=/usr/local/powerpc-linux/bin/nm export NM
+ RANLIB=/usr/local/powerpc-linux/bin/ranlib export RANLIB
+ SIZE=/usr/local/powerpc-linux/bin/size export SIZE
+ ADDR2LINE=/usr/local/powerpc-linux/bin/addr2line export ADDR2LINE
+ GASP=/usr/local/powerpc-linux/bin/gasp export GASP
+ OBJCOPY=/usr/local/powerpc-linux/bin/objcopy export OBJCOPY
+ OBJDUMP=/usr/local/powerpc-linux/bin/objdump export OBJDUMP
+ STRINGS=/usr/local/powerpc-linux/bin/strings export STRINGS
+ STRIP=/usr/local/powerpc-linux/bin/strip export STRIP
+ HOSTED_CC=/usr/bin/cc export HOSTED_CC
+ NOGCCERROR=yes export NOGCCERROR
+ OBJECT_FMT=ELF export OBJECT_FMT
+ MAKE=gmake -f Makefile
+ export MAKE
+ set -x
+ exec gmake -f Makefile
/usr/local/powerpc-linux/bin/gcc -c startup.s -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c main.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c serial.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c lib.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c thread.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c syscall.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c memory.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c idle.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c extintr.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c kozos.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c command.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c crt1.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c crti.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/gcc -c crtn.c -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS
/usr/local/powerpc-linux/bin/ar ruc libc.a crt1.o crti.o crtn.o
/usr/local/powerpc-linux/bin/gcc startup.o main.o serial.o lib.o thread.o syscall.o memory.o idle.o extintr.o kozos.o command.o -o sample -Wall -nostdinc -fno-builtin -msoft-float -I../uboot/u-boot.git/include -g -DKOZOS -static -T ld.scr -L.
hiroaki@teapot:~/tb0286/kozos>%
hiroaki@teapot:~/tb0286/kozos>%
hiroaki@teapot:~/tb0286/kozos>% ./make.sh image
+ LOCAL=/usr/local
+ TARGET=powerpc-linux
+ TARGETDIR=/usr/local/powerpc-linux
+ PATH=/usr/local/powerpc-linux/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/usr/pkg/sbin:/usr/pkg/bin:/usr/X11R6/sbin:/usr/X11R6/bin:/home/hiroaki/bin: export PATH
+ GCC_EXEC_PREFIX=/usr/local/powerpc-linux/bin export GCC_EXEC_PREFIX
+ AR=/usr/local/powerpc-linux/bin/ar export AR
+ AS=/usr/local/powerpc-linux/bin/as export AS
+ CC=/usr/local/powerpc-linux/bin/gcc export CC
+ /usr/local/powerpc-linux/bin/gcc -print-prog-name=cpp
+ CPP=cpp export CPP
+ CXX=/usr/local/powerpc-linux/bin/gcc export CXX
+ FC=/usr/local/powerpc-linux/bin/f77 export FC
+ LD=/usr/local/powerpc-linux/bin/ld export LD
+ NM=/usr/local/powerpc-linux/bin/nm export NM
+ RANLIB=/usr/local/powerpc-linux/bin/ranlib export RANLIB
+ SIZE=/usr/local/powerpc-linux/bin/size export SIZE
+ ADDR2LINE=/usr/local/powerpc-linux/bin/addr2line export ADDR2LINE
+ GASP=/usr/local/powerpc-linux/bin/gasp export GASP
+ OBJCOPY=/usr/local/powerpc-linux/bin/objcopy export OBJCOPY
+ OBJDUMP=/usr/local/powerpc-linux/bin/objdump export OBJDUMP
+ STRINGS=/usr/local/powerpc-linux/bin/strings export STRINGS
+ STRIP=/usr/local/powerpc-linux/bin/strip export STRIP
+ HOSTED_CC=/usr/bin/cc export HOSTED_CC
+ NOGCCERROR=yes export NOGCCERROR
+ OBJECT_FMT=ELF export OBJECT_FMT
+ MAKE=gmake -f Makefile
+ export MAKE
+ set -x
+ exec gmake -f Makefile image
#cp sample sample.bin
powerpc-linux-objcopy -O binary sample sample.bin
../uboot/u-boot.git/tools/mkimage \
-A ppc -O u-boot -T standalone -C none \
-n "smplimg" -a 0x00040000 -e 0x00040000 \
-d sample.bin sample.img
Image Name: smplimg
Created: Sun Mar 8 22:42:46 2009
Image Type: PowerPC U-Boot Standalone Program (uncompressed)
Data Size: 16036 Bytes = 15.66 kB = 0.02 MB
Load Address: 0x00040000
Entry Point: 0x00040000
hiroaki@teapot:~/tb0286/kozos>%

で,ボードに電源入れてファームウエアをインストールして起動する.

# bootm 80000
## Booting image at 00080000 ...
Image Name: smplimg
Created: 2009-03-08 11:13:35 UTC
Image Type: PowerPC U-Boot Standalone Program (uncompressed)
Data Size: 16036 Bytes = 15.7 kB
Load Address: 00040000
Entry Point: 00040000
OK
puts ok
printf ok
PVR = 80822014
MSR = 0000b002
MSR = 00003002
MBAR (from SPR) = f0000000
MBAR (from MBAR) = 0000f000
MBAR (from bd) = f0000000
IBPclock = 66000000
baudrate = 115200
MSR = 00003002
MSR_EE set OK
> echo aaa
aaa
OK
> echo bbb
bbb
OK
> echo test
test
OK
> threads
extintr
idle
command
OK
>

おー,動いた.echoコマンドやthreadsコマンドにきちんと応答できている.

なんかこれいいね.実ハードで動くと,シミュレータ上で動かすのとはまた違った感動があるものだなあ.