次のページ 前のページ 目次へ

8. x86 との非互換

8.1 I/O アドレス空間

Z80 や x86 以外のほとんどのアーキテクチャでは,I/O アドレス空間,という概念は存在しません. このため,ターゲットに対し inb() や outb() などの関数をどう対応させるか,ということが問題となります.

単なるメモリマップド I/O の場合はまだいいのですが,PCI バスや PCMCIA バスなどではかなり難儀するでしょう. x86 の場合は,バスアドレスがそのまま I/O 空間にマップされていますが,そのほかのアーキテクチャでは別のアドレスにマップされていたりします. さらに困ったことに,これらのバスでは I/O アドレス空間とメモリアドレス空間を扱わなくてはなりません. ターゲット CPU アーキテクチャにはそのような概念は無いにも関わらず.

8.2 エンディアン

が問題になる場合もあるかもしれません.

最近の CPU の場合,エンディアンフリーである場合が多いのですが,Linux を移植する場合は,できれば(選択の余地があるならば)リトルエンディアンがよいでしょう. Linux カーネル自体は,PowerPC や SPARC などでも動作実績はあり,エンディ アンに関する問題はありません. が,PCI バスや PCMCIA などのデバイスまわりは PC/AT アーキテクチャが基 準となっており,リトルエンディアンが基本になっています.

8.3 パイプライン

CPU 内部のパイプライン処理が問題になる場合もあります. パイプラインの深さなどによっては,プログラム上の read / write と,バス上の実際の read cycle / write cycle の順番が入れ違ってしまう場合もあります. USB ホストドライバ はこの例です.

この場合は,例えば MIPS ならば sync 命令や多連続の nop などでパイプラインをフラッシュしてやります.

8.4 アラインメント・パディング

アドレスに対してワード境界をまたがるアクセスを許していない CPU が多数あります. RISC 系の CPU では,大抵がそうです.

ワード境界をまたがるアクセス,とは例えば

のことです. このようなアクセスを許していない CPU でこのようなアクセスを行なうと,例外を発生します.

このため,構造体には,サイズ調整のための詰めもの(パディング)がされることがあります. パディングとは,例えば

struct sample_t {
char a;
int b;
};
という構造体の場合,CISC 系の場合,
addr+0

char a

addr+1,2,3,4

int b

という割当がなされますが,RISC 系の場合,
addr+0

char a

addr+4,5,6,7

int b

と割り当てられ,addr+1,2,3 は構造体のメンバは割り当てられません.

これが問題になるのは,例えば受信ストリームを構造体のポインタでキャストして,解釈を行なおうとする場合です.

8.5 キャッシュコヒーレンシ

x86 系 CPU の場合,キャッシュコントローラのバススヌープ機能により,デバイスが RAM へ DMA 転送を行なった場合でも,RAM の内容とキャッシュの内容が食い違うことはありません.

が,バススヌープ機能が無い CPU が多々あります. この場合は,DMA 転送前に(少なくとも DMA 対象領域の)キャッシュの内容 をメモリに書き出したり破棄する処理が明示的に必要となります.

8.6 ioperm(), iopl()

あたりが実装されていない場合が多々あります. ハードウェア直叩きのプログラムは困りますね. あと,Linux の X サーバは iopl() を使用しているので,これも困ります. ただし,フレームバッファはデバイスファイル経由のアクセスですが.

この場合の対処法としては,

というものがあります.

8.7

ARM の場合,char = unsigned char であり,そのほかの CPU では char = signed char です. このこと自体は,ANSI 規格ではコンパイラの実装依存であるため,間違いではありません.

このため,ARM Linux でのコンパイルオプションには -fsigned-char (char を signed char として解釈する)オプションが付いています.

ユーザランドで動かすプログラムについても同様の注意が必要です.


次のページ 前のページ 目次へ