ハードウェアを意識したプログラミングの基礎(前編)
● 0x1234ABCDか0xCDAB3412か
それでは実際の例を示し,どんな時にエンディアンが問題になるかを見ていきましょう.
仮に,あるデバイスのFIFOから32ビットのデータを読み込まなければならないとします.このFIFOからデータを受け取るために,x86のデバイス・ドライバ・プログラマは以下のようなコードを書きました.
u32 val = *(u32 *)FIFO_ADDR;
どうやら,このデバイス・ドライバはx86上では正しく動いたようです.しかし,このコードをそのままビッグ・エンディアンに設定したPowerPCに移植したところ,思ったように動かなくなりました.
この問題を簡単にアプリケーション(C言語のソース・コード)で表したものがリスト1です.
1: #include <stdio.h>
2: #include <linux/types.h>
3:
4: int main()
5: {
6: char fifo[] = {0x12, 0x34, 0xAB, 0xCD};
7: __u32 val;
8:
9: val = *(__u32 *)fifo;
10:
11: printf("0x%Xエn", val);
12: return 0;
13: }
リスト1 どちらのエンディアンか
2行目のヘッダは,コードの見た目をLinuxのデバイス・ドライバに似せるために使っています注2.デバイス・ドライバ内で使える「u32」という型は,ユーザ・ランドではPOSIXのネーム・スペースを侵害してしまいます.そこで「__」を先頭に付けてネーム・スペースを汚さないように変更したものが,アプリケーションから使えるようになっています.u32はUnsigned(符号なし)の32ビット変数という意味です.
注2;カーネルのヘッダ・ファイルをアプリケーションでインクルードするのはあまり良くない.自分の責任で使用すること.
6行目では,FIFOの代わりにスタック上にバッファをとっています.初期値は,アドレスの小さい方から0x12 0x34 0xAB 0xCDとなるようにしました.
9行目で,FIFOから値を読み出して,変数valに代入します.そして読み出した値をprintf()を使って出力しています.
本当のデバイス・ドライバであれば,受け取ったデータを処理したり,初期化関数内でデバイスIDを読み出して比較したり(この場合,FIFOではなくデバイスのレジスタだが)する場合に似たようなことを行います.さて,上記のコードをx86上でコンパイルして実行すると,
$ ./a.out
0xCDAB3412
と表示されます.これはx86がリトル・エンディアンだからです.それではPowerPCで実行するとどうなるのでしょうか?
$ ./a.out
0x1234ABCD
となりました.x86と違い,PowerPCはビッグ・エンディアンなので,メモリに並んだように値が読めます.混乱した人はもう一度,図2と見比べてみてください.
● マクロle32_to_cpu
さて,普通に読み込むと値が異なることは分かりました.しかし,このままではデバイス・ドライバを移植することがとても面倒です.Linuxではこの問題を解決するために,エンディアンによるバイト・オーダの違いを吸収するマクロを用意しています.マクロはエンディアンごとに,
include/linux/byteorder/little_endian.h
include/linux/byteorder/big_endian.h
で定義されています.リトル・エンディアンで32ビット書き込みを行う場合はcpu_to_le32()が,読み込みの場合は le32_to_cpu()が使えます.このほかにも,ビッグ・エンディアン用のbe32_to_cpu()とcpu_to_be32(),および16ビット用や64ビット用のマクロも用意されています.8ビット・アクセスについては,エンディアンに関係ないのでマクロは用意されていません.
マクロを使うことで,コードそのものの移植性が高くなります.しかし,上記のヘッダを直接インクルードしてしまうと,結局デバイス・ドライバをエンディアンに依存させてしまうことになります.そこで,デバイス・ドライバからは,上記のヘッダ・ファイルではなく,
include/asm/byteorder.h
をインクルードします.asm/byteorder.hはビッグ・エンディアンがターゲットであればlinux/byteorder/big_endian.hを,リトル・エンディアンであればlinux/byteorder/little_endian.hをインクルードするようになっています.これでエンディアンに依存させずに,デバイス・ドライバで必要なマクロが使えるようになります.