出張で和歌山県の白浜に行ってきました。新宿から和歌山までは夜行バスで行きました。水曜どうでしょうでは結構なやられっぷりで心配していたんですが、これが以外と快適でした。むしろ、和歌山駅から白浜駅までの特急くろしおの乗り心地がひどかったくらいです。

Sigma F1.4 EX DC

Sigma 30mm F1.4 EX DC

PENTAX DA 14mm F2.8 ED

PENTAX DA 14mm F2.8 ED

PENTAX DA 14mm F2.8 ED

PENTAX DA 14mm F2.8 ED

PENTAX DA 14mm F2.8

PENTAX DA 14mm F2.8 ED

PENTAX DA 14mm F2.8 ED

PENTAX DA 14mm F2.8 ED

白良浜の砂は本当に白かったです。

割り込みコントローラ

SH7780では割り込みの受付時にINTEVTレジスタ(メモリ割り付けレジスタ)を読むことでどの要因で割り込みが発生したのか特定できるようになっている。割り込みコントローラ(ハードウェア)は、タイマーやDMACなどから割り込みの要求が入るとINTEVTにその要求に割り当てられた数字を書き込む。割り込みハンドラ(プログラム)はINTEVTレジスタを読み込み、スケジューリングやIRQの転送などの処理へ分岐していく。

割り込みの要因は四種類に大別できる。NMI、IRQ、IRL、内蔵モジュールである。NMIはNon Maskable Interruptで、マスクできない、つまり、プロセッサがどのような状態にあっても入ってくる割り込みである。IRQはInterrupt Requestで、一般に周辺機器を制御するための割り込みである。SH7780には8チャネルのIRQ端子があり、それぞれ独立して割り込みの状態を設定できる。IRLはIRQと同じように周辺機器を制御するための割り込みなのだが、割り込みをチャネルではなくレベル(あるいはチャネルの状態のパターン)で分類する。IRLを使うと効果的な場面はよくわからない。内蔵モジュールの割り込みは、タイマー、DMAC、シリアルインタフェースなどからの割り込みである。

割り込みには優先順位をつけることが出来る。レベル16が最高で、レベル1が最低となっている。レベル0では割り込みが受け付けられない。割り込みの優先順位は同時刻に発生した割り込みを開発者の思うとおりに制御するために必要である。たとえば、タイマー割り込みの優先度が高ければ、スケジューリングの遅延を避けることが出来、結果として、実時間性を向上させることが出来る。

割り込み要因の考慮

先述のように割り込みの要因は四つある。NMIはカーネルで処理する。したがって、ユーザー空間に転送しなければならない割り込みは、大別すると外部割り込み(IRQもしくはIRL)と内部割り込み(内蔵モジュール)の二種類になる。L4のAPIでは割り込みは一つの空間にまとめられている。IA32やARMでは割り込みの空間は一種類なので良かったが、二種類の割り込みの空間があるSH4Aではカーネルはどのように処理したらよいのだろうか。

カーネル内部ではIRQを管理する配列がある。IRQそれぞれに対し、一要素を割り当てる。SH4Aでは12ビットの例外コードによって、外部割り込み、内部割り込みに関わらず、要因を区別する。これらの割り込みは合計すると68種類にもなる。例外コードの内、下位5ビットは使われていない。つまり、ゼロだ。下位5ビットを算術シフト演算で取り除き、オフセット値を引けば、0からの通し番号に変換できる。しかし、例外コードをよく見てみると、所々番号が飛んでいるところがある。なんともいやらしいなぁ。

外部割り込みだけなら話は簡単だ。しかし、デバイスドライバの処理に必要なDMACやシリアルポートの割り込みは内部割り込みだ。

とりあえずの解法として、飛び石の番号を無視して配列を確保してしまうことにした。例外コードは0×200から始まり0xFE0までなので、0×20毎に例外コードがあるとすれば112要素必要になる。実際には例外コードは68種類しかないので54要素分は不要になる。各要素は12バイトのデータ構造なので、648バイトは無駄になる。

割り込みコントローラの設定

  • 端子モードはIRQを使用する(IRLモードは使用しない。効果的な使い方を知らないので。)
  • IRQ0からIRQ7はオフ(ユーザプロセスからの要求に従ってオン)
  • TMU0はオン

SH4Aは二種類のタイマーを提供する。TMUは独立したチャンネルが六つあり、割り込みも独立して入る。CMTにもチャンネルはあるが、割り込みは共通している。特に理由はなくTMU0をスレッドディスパッチ用タイマーとして選択する。

カーネル内の割り込みハンドラ

例外処理ルーチンの先頭アドレスは各割り込みで共通である。なので、割り込み要因を識別するためにINTEVTをオフセットとして分岐する。

  • IRQをIPCに変換する (IRQ0からIRQ7)
  • スケジューリングする (TMU)

カーネル割り込みハンドラの中でやらなければいけないこと

1.IRQを配送する

IRQはユーザプロセスで処理されるので、そのためにカーネルは受け取った割り込み要求をユーザプロセスに配送しなければならない。例:simplesoc_handle_irq_rescheduleを呼び出す。(rescheduleの意味は何?)

2.スケジューリングする

handle_timer_interruptを呼び出す。

カーネル割り込みハンドラの中でやらないこと

シリアルインタフェースの割り込み処理など、その他の割り込み。

割り込みAPI

割り込みハンドラの登録

割り込み処理

設計

GCCの呼び出し規約を参考にしてシステムコールの呼び出し規約を定義する。

GCC(ルネサス?)の呼び出し規約

R0:返却値
R1からR3:空
R4からR7:引数
R8からR13:呼び出し先が退避する
R14:フレームポインタ、呼び出し先が退避する
R15:スタックポインタ
PR:サブルーチンの戻りアドレス

参考1:gcc/config/sh/sh.h
参考2:http://www.ertl.jp/~takayuki/readings/info/no04.html

呼び出し規約

第一引数:R4
第二引数:R5
第三引数:R6
第四引数:R7
第五引数:R1
第六引数:R2
第七引数:R3
第八引数:R9
システムコール番号:R8
返却値:R0

ユーザプログラムはR8にシステムコール番号を格納し、TRAPA命令を発行する。システムコールの引数は指定のレジスタを介して渡される。システムコールは8個までの引数を取ることが出来る。

TRAPAはイミディエイトオペランドを持つので、このオペランドを使ってシステムコール番号を指定することも可能だ。

TRAPA

PC+2、SR、R15の値をSPC、SSR、SGRに退避し、イミディエートデータをTRAレジスタに格納する。処理モードを特権モードに切り替え、BANK1レジスタを選択し、例外コード0×160をEXPEVTレジスタに書き込む。処理は例外ハンドラに分岐する。

システムコールの流れ

ユーザー側の処理(呼び出し前)
・前提:以下の処理は関数の先頭で行う。(R4からR7以外は自由に使える状態であること)
・スタックから第五引数から第八引数をR0からR3に格納する。
・R8にシステムコール番号を格納する
・TRAPAを発行する。
・R10からR14は使わない

カーネル側の処理
・TCBにシステムレジスタを退避する
・カーネルスタックに切り替える
・バンク1からバンク0に切り替える(ユーザーのレジスタに切り替える)
・R0からR3をスタックに格納する
・コンティニュエーションをPRに格納
・システムコールを呼び出す

コンティニュエーション
・TCBからシステムレジスタを復帰する
・ユーザースタックに切り替える
・RTEを発行する

ユーザー側の処理(呼び出し後)
・呼び出し元に戻る。

コンティニュエーションの利用

カーネルスタックの管理を簡単にするためにL4はコンティニュエーションを多用している。

IPCの最適化

マイクロカーネルのシステムで最も大きなボトルネックとなるのがIPCである。たとえば、モノリシックカーネルではサブシステム間のコンテキストスイッチが起きない。それに対し、マイクロカーネルではサブシステムがユーザレベルのプロセスとして動くのでサブシステム間でコンテキストスイッチが起こる。サブシステムを細かく分割すれば安全性は高まるが、より多くのIPCが発生し、結果としてシステムの性能は低下する。

IPCはシステムコールであるが、トラップハンドラ内でIPC専用のパスを用意することで最適化する。

はじめに

まずは例外ハンドラの実装から。例外ハンドラが処理する事象は三つある。

  • システムコール
  • 例外
  • 割り込み

システムコールは例外の一種として扱われる。SH-4Aのマニュアルでは、第5章に例外処理の解説がある。詳細はマニュアルを参照されたい。

システムコール

システムコールとはユーザプログラムからのカーネルの機能の呼び出しのことである。L4マイクロカーネルは自身を特権モードで動かし、ユーザプログラムを非特権モードで動かす。一般に非特権モードで動いているプログラムは特権モードで動いているプログラムよりも出来ることが少ないので、非特権モードのプログラムが特別な処理を使用とする場合には、特権モードで動くプログラムにお願いする必要がある。このお願いする方法が多くのカーネルではシステムコールとして提供されている。

SH-4AではTRAPAという命令でシステムコールを発行する。ユーザープログラムがTRAPAという命令を発行するとCPUのモードは非特権モードから特権モードに自動的に切り替わる。その上で、プログラムカウンタの値をあらかじめ設定された番地に変更する。この番地に特権モードで動くプログラム、すなわちシステムコールを処理するプログラムを置いておけば、自動的に実行される。特権モードで動くプログラムはRTEという命令を発行し、非特権モードのプログラムに戻る。RTEは自動的にCPUのモードを特権モードから非特権モードに切り替える。

例外

例外はユーザプログラムやカーネルが誤った操作をしたときに発生する。たとえば、書き込みが禁止されている領域にデータを書き込もうとした場合や、0で割り算をしようとした場合などである。この場合にも、システムコールと同じように、CPUはプログラムカウンタを特定の番地に変更する。この場所に例外を処理するプログラムを置いておくことで、例外の後片付けをすることが出来る。

TLBミス

TLBミスは特殊な例外だ。TLBはアドレス変換のデータを格納するキャッシュである。あるCPUではTLBへのデータの格納はハードウェアが自動的に行ってしまう。SH-4Aにはソフトウェアで設定できるTLBが搭載されている。したがって、TLBへデータを格納するには適切なプログラムを呼び出してやる必要がある。このプログラムを呼び出すためにTLBミスという例外がある。TLBミスは変換したいアドレスがTLBに見つけられないと発生する。

割り込み

割り込みとは周辺デバイスを処理するための仕組みである。たとえばキーボードのキーを押すと割り込みが発生する。L4は割り込み処理のほとんどをユーザープログラムで処理するような構造になっている。カーネルは割り込みを受け取り、それを適切な形でユーザープログラムに転送する。

設計

SH-4Aの例外処理

SH-4Aの例外の多くは、VBR(ベクタベースアドレス)に配置されたプログラムで処理されるようになっている。VBRはソフトウェアで自由に設定できる。VBRに設定できる例外処理プログラム(例外ハンドラ)はそのアドレスから三つに分類される。

一般例外(オフセット0×100):多くの例外がここで処理される。システムコールを発行するTRAPAもここで処理される。処理の内容としては大きく三つに分けることが出来る。

  1. システムコール:システムコールを実行
  2. ページフォルト:ページャに転送
  3. エラー:カーネルで処理、もしくはページャに転送

一般例外のハンドラでは、この場合分けを行う。詳細な処理はそれぞれ専用の関数をハンドラのコンテキストで呼び出し、処理を任せる。

命令実行前ユーザーブレーク・命令実行後ユーザーブレーク
デバッガに転送

命令TLB保護違反例外(故障検知)
アクセスがITLBの保護情報に反する場合に発生する。つまり、ユーザモードで特権領域のコードにアクセスすると発生する。 特権領域にあるのはカーネルコードだけなはずだから、これはデバッガに落ちるか、プロセスの強制終了になる。

一般不当命令例外・スロット不当命令例外

命令アドレスエラー・データアドレスエラー(故障検知)
データ境界以外からの命令フェッチもしくはアクセス、またはユーザーモードでの特権領域へのアクセスで発生する。

データTLB保護違反例外(故障検知、もしくはページフォルトに変換)
アクセスがUTLBの保護情報に反する場合に発生する。これは二つの場合が考えられる。一つ目は、ユーザプロセスが特権領域のアドレスにアクセスした場合、もう一つは、ユーザプロセスが非特権領域のアドレスで許可されていないアクセスをした場合、もしくはカーネルプロセスが特権領域のアドレスで許可されていないアクセスをした場合である。後者は例えば、書き込みの許可されていないアドレスに書き込みアクセスをした場合である。 特権領域へのアクセスは、システム全体で禁止されているので、そのプロセスは強制終了させられる。非特権領域でのこの例外はページフォルトとしてページャに転送される。

FPU例外
例外としてユーザプロセスに転送する。

初期ページ書き込み例外(ページフォルトに変換)
ストアアクセスでTLBにヒットしたが、ダーティビットはゼロの時に発生する。つまり、アドレスはTLBに格納されているのだが、そのアドレスにまだ書き込みがされていない状態で発生する。何らかの理由によってTLBエントリだけを設定していたり、アドレスに対して読み込みだけしていたりする場合にこの状態になる。 L4上ではこの例外は上がってきてはいけない。万が一この例外が補足された場合には書き込み要求のページフォルトに変換する。

無条件トラップ
システムコール

TLBミス(オフセット0×400):TLBミスを処理する。多くの場合は、ページフォルトとしてページャに転送されるものと予想する。

割り込み(オフセット0×600):割り込みを処理する。ユーザーレベルのプログラムに転送する。

例外処理中の例外

例外の処理はソフトウェアで行われるため、例外処理中に他の例外事象が発生する可能性がある。これに対し、例外処理中に他の例外処理をする仕組みと、例外処理中に他の例外処理を禁止する仕組みを考える必要がある。

CPUは例外によって特権モードに移行する。SH-4Aは二種類のレジスタセット(レジスタバンク)を提供しており、モードによって自動的に切り替 わる。バンク0のレジスタR0からR7はユーザーモードのレジスタR0からR7に割り当てられる。バンク1のレジスタR0からR7は特権モードのレジスタ R0からR7に割り当てられる。バンク0のレジスタは特権モードでもアクセスしたり、R0からR7に割り当てることが可能だが、バンク1のレジスタは特権 モードでしか操作できない。例外ハンドラ起動時にはレジスタR0からR7はバンク1のR0からR7に割り当てられている。R8からR15はモードによる切 り替えはない。

例外処理中に他の例外を処理するためにはバンクを1から0に切り替えておく必要がある。

例外の優先順位

例外には優先順位がつけられており、これを実装にどのように反映すべきか考える必要がある。

実装

バンクの切り替え

バンクの切り替えはSRレジスタのMDビットの操作で行う。SRレジスタの操作には少なくとも一つの汎用レジスタ(R0からR15のいずれか)の使用を伴うので、場合によっては汎用レジスタの内容を退避する必要がある。

コンテキストの退避と復旧

例外処理以前の状態に復帰するためにコンテキストを退避しておきたい場合がある。多くのカーネルの実装と同じく、ここでもレジスタの値をスタックに保存する。

ハンドラの配置

一般例外、TLBミス、割り込みの各ハンドラを適切なアドレスに配置する必要がある。これにはbalign命令を使う。

一般例外

無条件トラップ:システムコールハンドラへ分岐。システムコールを実行する。

TLB保護違反例外と初期ページ書き込み例外:TLBハンドラへ分岐。条件によってはページフォルトに変換する。

ユーザーブレーク:カーネルデバッガへ分岐。

その他:一般例外ハンドラへ分岐。プロセスの強制終了などを判断する。

Machineクラスはビルドの設定に使うクラスである。tools/machines.py内に基底クラスが定義されている。

各メンバを解説する。等号は初期値

virtual = True

ターゲットの種類が仮想的なものであればTrue、そうでなければFalseを設定する。仮想的なターゲットはバイナリを生成しない。

default_toolchain = None

標準のクロスコンパイラを指定する。ツールチェインの名前はtools/toolchains.pyで定義する。

as_flags = []

アセンブラに渡すフラグを設定する。

c_flags = []

Cコンパイラに渡すフラグを設定する。

cpp_flags = []

Cプリプロセッサに渡すフラグを設定する。

cpp_defines = []

Cプリプロセッサに渡す定義を設定する。

cxx_flags = []

C++コンパイラに渡すフラグを設定する。

link_flags = []

リンカに渡すフラグを設定する。

drivers = []

カーネルと共にビルドするドライバのリスト。

device_core = None

iguanaのデバイスドライバフレームワークで使われる。

wordsize = None

標準のワードサイズ。単位はビット。

endian = None

“little”か”big”を指定する。

timer_driver = None

タイマードライバーの名前。

serial_driver = None

シリアルポートドライバーの名前

cpu = “”

CPUの名前。

memory = { ‘virtual’ : [], ‘physical’ : [], ‘rom’ : [], ‘tcm’ : [], }

有効なアドレスの範囲を設定する。

base_vaddr = 0×100000

プログラムを配置するときのベ基準アドレス。

preferred_alignment = 0×10000

標準のアラインメントサイズ。起動時のプログラムの配置に使われる。

min_alignment = 0×1000

最小のアラインメントサイズ。

smp = False

マルチプロセッサ構成かどうか。

zero_bss = False

BSSをゼロクリアするかどうか。

boot_binary = False

ブートイメージを生成するかどうか。

copy_elf = False

ELFイメージをビルドディレクトリにコピーするかどうか。

cust = “okl”

顧客の名前

default_feature_profile = “EXTRA”

simulate_cache = False

ターゲット

今回の移植ターゲットはSH2007(http://sh2000.sh-linux.org)。SH-4A(http://japan.renesas.com)のインストラクションセットを持つ。まずはシングルプロセッサのSH2007がターゲットである。SH2007はSH7780を搭載する。

OKL4ソースツリー

OKL4 3.0をhttp://wiki.ok-labs.com/Release/3.0からダウンロードする。

クロスコンパイラ

CrossToolsを利用する。http://www.kegel.com/crosstool/からダウンロードする。現時点でバージョンは0.43。クロスコンパイラのビルドは基本的に説明通り。gccのバージョンは3.4.5とした。Ubuntuの場合、これ以外にビルトに使うコンパイラを4.2とする必要があるかもしれない。

demo-sh4.shの変更点

gcc 4.2を指定することと、3.4.5をビルドするよう指定すること。

export CC=gcc-4.2
eval `cat sh4.dat gcc-3.4.5-glibc-2.3.5.dat` sh all.sh --notest

ディレクトリの追加

アーキテクチャ依存コードを格納するディレクトリを追加する。現段階ではマイクロカーネルと周辺ライブラリのみの移植を目指す。追加したディレクトリは以下のようになる。

arch/sh/ktest
arch/sh/libs/atomic_ops/include
arch/sh/libs/atomic_ops/src
arch/sh/libs/c/crt
arch/sh/libs/c/include
arch/sh/libs/c/src
arch/sh/libs/compat/include
arch/sh/libs/kernel/include
arch/sh/libs/l4/cpu/sh7780
arch/sh/libs/l4/include
arch/sh/libs/l4/src
arch/sh/libs/okl4/include
arch/sh/libs/soc/include
arch/sh/pistachio/cpu/sh7780/include
arch/sh/pistachio/include
arch/sh/pistachio/kdb
arch/sh/pistachio/src/gnu
arch/sh/tools
platform/sh2007/pistachio/include
platform/sh2007/pistachio/kdb
platform/sh2007/pistachio/src
platform/sh2007/tools

ビルドスクリプトの変更

OKL4のビルドは以下のようなコマンドで実行する。

./tools/build.py PYFREEZE=false MACHINE=pxa PROJECT=iguana

これを以下のようにしたい。

./tools/build.py PYFREEZE=false MACHINE=sh2007 PROJECT=ktest

まずは、SHクロスコンパイラを起動するようにビルドスクリプトを変更する。編集するのはtools/toolchains.py。クラスsh_toolchainを定義し、登録する。

class sh_toolchain(generic_gcc):
    def __init__(self, *args):
        generic_gcc.__init__(self, *args)
        # gccのフラグに追加
        self.dict["CCFLAGS"] += ["-m4-nofpu"]
        # g++のフラグに追加
        self.dict["CXXFLAGS"] += ["-m4-nofpu"]

gnu_sh_toolchain = sh_toolchain("sh-linux-")

アーキテクチャ毎の設定

アーキテクチャ毎の設定はarch/sh/tools/machines.pyに書く。各アーキテクチャはクラスとして表現される。ここでクロスコンパイラを指定する。

from machines import Machine, Region
from toolchains import gnu_sh_toolchain

class sh(Machine):
    # サポートしているページサイズ(1KiB, 4Kib, 64KiB, 1MiB)
    page_sizes = [0x400, 0x1000, 0x10000, 0x100000]
    # プログラムをアラインするときの標準のサイズ
    preferred_alignment = 0x10000L
    # 標準のワードサイズ
    wordsize = 32
    # アーキテクチャの名前
    #(アーキテクチャ依存コードを格納するディレクトリの名前)
    arch = "sh"
    # エンディアン
    endian = "little"
    # エミュレータを指定する。SHのエミュレータがないのでここは空。
    default_method = ''
    # 標準のクロスコンパイラ
    default_toolchain = gnu_sh_toolchain

# SH-4 (SH7750 series)
class sh4(sh):
    # 有効なアドレス範囲のリスト。tools/machines.pyで定義されている。
    memory = sh.memory.copy()
    # 有効な仮想アドレスの範囲を定義
    memory['virtual'] = [Region(0x1000, 0x7ef00000)]
    # アーキテクチャのバージョン(SH4なので4とした)
    arch_version = 4

# SH-4A (SH7780 series)
class sh4a(sh):
    memory = sh.memory.copy();
    memory['virtual'] = [Region(0x1000, 0x7ef00000)]
    # アーキテクチャのバージョン
    #(アルファベットを指定できないので4とした)
    arch_version = 4

class sh7780(sh4a):
    # CPU依存コードを格納するディレクトリの名前
    cpu = "sh7780"

class sh7785(sh4a):
    cpu = "sh7785"

プラットフォーム毎の設定

プラットフォーム毎の設定はplatform/tools/machines.pyに記述する。各プラットフォームはクラスとして表現される。

import copy

class sh2007(sh7780):
    virtual = False
    # ビルドスクリプトを起動するときにMACHINEで指定する文字列
    platform = "sh2007"
    # プラットフォーム依存コードを格納するディレクトリ
    platform_dir = "sh2007"
    # タイマードライバーの名前
    timer_driver = ""
    # シリアルポートドライバーの名前
    serial_driver = ""
    # ビルドするドライバーのリスト
    drivers  [timer_driver, serial_driver] + sh7780.drivers

    memory = sh7780.memory.copy()
    # RAMがマッピングされる仮想アドレス
    memory['physical'] = [Region(0x88000000, 0x90000000)]
    # ROMがマッピングされる仮想アドレス
    memory['rom'] = [Region(0xA0000000, 0xA80000000)]
    # プリプロセッサに与える文字列。ソースコード内で
    # プラットフォーム依存部を切り替えるのに使うことが出来る。
    cpp_defines = sh7780.cpp_defines = [("PLATFORM_SH2007", 1)]

pyelf

pyelfはELFファイルを編集するプログラムである。ELFとは実行可能ファイルの形式の一種である。ELFファイルには機種依存の部分がある。したがって、IA32アーキテクチャ、ARMアーキテクチャ、SHアーキテクチャのELFファイルはそれぞれ微妙に異なる。pyelfはELFファイルを編集するのでこの微妙な違いを理解しなければならない。機種依存のコードは、tools/pyelf/elf/abiに格納する。SHのコードは他のファイルに倣ってsh.pyとする。

ELFファイルの機種依存部の形式はgccでも定義されているので、gccのソースコードから必要な部分だけをコピーしてくる。

ディレクトリ構成

さて、どうやってOKL4を新しいアーキテクチャに移植するのか、それを考えなければならない。OKL4のソースツリーを見ると、アーキテクチャ依存コードは、archディレクトリとplatformディレクトリにまとめられている。

archディレクトリ

archディレクトリにはプロセッサ依存コード、つまり、ARMプロセッサのコードであれば、archディレクトリの下にarmというディレクトリがあり、そこにARM関連のソースコードが格納される。プロセッサ毎の細かな違い、たとえば、ARMコアはバージョン毎で機能が異なるのだが、このような違いもこのディレクトリ内のソースコードで吸収される。

platformディレクトリ

platformディレクトリはボード依存コードが格納される。同じARMプロセッサを使っていてもボードによっては異なる周辺装置を搭載しているので、その違いをこのディレクトリ内で吸収する。

その他

tools/pyelf/elf/abiディレクトリにもアーキテクチャ依存コードがある。

方針

ここでは全体的な方針について簡単に議論する。今回の移植の方針はズバリ、ツールに助けてもらう、だ。とにかくコンパイラやリンカのエラーをつぶしていく。その上で人間が見て足りない部分を補い、最終的には実行時のエラーを見て修正していく。人間は間違えたり、見逃したりするということを前提にすれば、コンパイラやリンカのようなツールを使って網羅的にエラーを発見していくことは効率がよいと考える。

ビルド環境を整える

まずは、ビルド環境を整える必要がある。ビルド環境がないとコンパイルすることすら出来ない。すなわち、コンパイルエラーやリンクエラーを見ることが出来ない。

  1. クロスコンパイラのインストール
  2. ビルドスクリプトの変更

ARMのコードを見ながら

移植の方針その一は、ARMのディレクトリ(arch/arm)を見ながらファイルを追加していく方法である。まずはARMのファイルを観察する。どのようなクラスがあり、どのようなコードが必要とされているのかを大まかに把握する。

コンパイルエラーを見ながら

ディレクトリやファイルを適当に追加したらコンパイルしてみる。コンパイルエラーを一つ一つ潰していく。ARMのソースコードを持ってきたときにはコンパイルできてしまう場合があるので、それで正しいのかどうか注意する。

リンクエラーを見ながら

コンパイルが通るようになったら、リンクエラーを見つつ、未定義の関数を実装していく。

導入

OKL4とはL4マイクロカーネルの系譜にあるマイクロカーネルの実装である。OKL4はオーストラリアの大学で開発が開始され、現在はOKLabという企業が開発を主導している。

動機

現在、OKL4が動作するのは公式にはARMプロセッサだけである。私自身のプロセッサの勉強も兼ねて、OKL4のSH4aへの移植を行う。

ソースコードを公開した。http://www.dcl.info.waseda.ac.jp/~ishikawa/okl4.html

博士論文の公聴会を木曜日にやった。これでようやく博士論文は一段落。公聴会では発表45分、討論45分で大体1時間30分くらいしゃべり通した。そのあとは、飲み会だった。普段1時間30分も話すことはないので興奮していたのか、夜は3時間くらいしか眠れなかった。その分、金曜の夜は眠くてたまらなかった。最近の日課だった「水曜どうでしょう」を見ることもなく9時頃には布団に入ってしまった。

自己欺瞞から自分を解放し、人間関係をよくするための提案が書かれてある。amazonの書評にもあったが、この本を読むと人の見方が変わる。これはとりもなおさず、今までの自分が多かれ少なかれ自己欺瞞に陥っていたということの証明でもあるのだが。

この本では人が自己欺瞞にとらわれている状態を「箱」に入っていると表現する。キャッチーな表現だが、これが時々文意をわかりづらくしている。内容としては、事例を元にした説明が多い。エッセンスだけをまとめたら三分の一くらいのページ数で説明できるのではないか。わたしは図書館で借りて読んだが、それで十分だろう。