Pylone Blog - タグ:linux
PowerPCボードADS512101へのLinuxカーネルの移植
Freescale社製PowerPCボードADS512101へLinuxカーネルを移植する手順を紹介します。
Freescale社ではADS512101用のLinuxBSPを公開しており、ソースコードから環境を構築することが可能ですが、Linuxカーネルのバージョンが2.6.24と若干古いため、比較的新しいバージョンを移植することが目的です。
予め
- PowerPC向けクロス開発ツール
- tftpサーバ
をインストールした環境を前提とします。
尚、使用するU-Boot、及びLinuxカーネルのバージョンは以下のとおりです。
- U-Boot 2009.1
- Linuxカーネル 2.6.28
U-Bootのビルド
オフィシャルサイトからソースコードを取得し、展開します。
% wget ftp://ftp.denx.de/pub/u-boot/u-boot-2009.01.tar.bz2 % tar xjfv u-boot-2009.01.tar.bz2
ターゲットにADS5121を指定してビルドします。
% cd u-boot-2009.01 % make ads5121_config % CROSS_COMPILE=powerpc-linux-gnu- make
Linuxカーネルのビルド
オフィシャルサイトからソースコードを取得し、展開します。
% wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.28.tar.bz2 % tar xjfv linux-2.6.28.tar.bz2
ADS5121ボード用(v2.6.28向け)のパイロン製パッチを取得・適用します
% wget http://code.pylone.jp/hg/linux-2.6-ads5121-mq/raw-file/tip/ads5121 % cd linux-2.6.28 % patch -p1 < ../ads5121
カーネル設定ファイルを設置後、必要に応じて設定を変更しビルドします(デフォルトでU-Boot形式が生成されます)。
% cp arch/powerpc/configs/ads5121_defconfig .config % ARCH=powerpc CROSS_COMPILE=powerpc-linux-gnu- make menuconfig % ARCH=powerpc CROSS_COMPILE=powerpc-linux-gnu- make
デバイスツリーのビルド
デバイスツリーとは簡単に説明すると、
- PowerPC/Linux固有の仕組みで、ボード依存のデバイス情報(I/Oアドレス等)を定義したもの
- デバイスツリーを定義したソースファイル(*.dts)をコンパイラ(dtc)にてコンパイルしバイナリファイル(*.dtb)を得る
- *.dtbファイルはLinuxカーネルイメージとは別にメモリ上に置かれ、Linuxカーネルはその内容からボードに関する各デバイス情報を把握する
といったものです。
デバイスツリーコンパイラはLinuxカーネルに含まれているため、以下の様にしてADS5121向けデバイスツリーソースをコンパイルします。
% cd (Linuxカーネルのディレクトリ)/arch/powerpc/boot % ./dtc -O dtb -o ads5121.dtb dts/mpc5121ads.dts
Flash更新
tftpに備えて、これまで生成したバイナリファイルを、tftpサーバのディレクトリ(/srv/tftp/とします)にコピーしておきます。
# cp (U-Bootのディレクトリ)/u-boot.bin /srv/tftp/ # cp (Linuxカーネルのディレクトリ)/arch/powerpc/boot/uImage /srv/tftp/ # cp (Linuxカーネルのディレクトリ)/arch/powerpc/boot/ads5121.dtb /srv/tftp/
シリアルコンソールとしてシリアルクロスケーブルをHostPCとボードを接続します。通信設定は以下のとおりです。
| ボーレート | 115200 |
|---|---|
| ビット長 | 8 |
| フロー制御 | なし |
| ストップビット | 1 |
ボードの電源を投入後、SW1を押下しボード標準のU-Bootを起動します。このとき、
Hit any key to stop autoboot:
のカウントダウンが開始されたらキャンセルします。
tftpに備え、必要に応じて環境変数を設定します。 (ここでは、tftpサーバとなるHostPCのIPアドレスを192.168.0.26、ボードのIPアドレスを192.168.0.120とします)
=> setenv netmask=255.255.255.0 => setenv ipaddr=192.168.0.120 => setenv serverip=192.168.0.26
先ほどビルドしたU-BootをFlashに書き込みます。FlashのI/O開始アドレスは 0xFC000000 で、内容は以下の様になっています。
| アドレス | セクタ番号 | 内容 |
|---|---|---|
| FC000000 | 0 | Protected |
| FC040000 | 1 | File system |
| FFC40000 | 241 | Linux カーネル |
| FFEC0000 | 251 | Device tree |
| FFF00000 | 252 | U-Boot |
| FFF40000 | 253 | U-Boot 環境変数 |
まず、更新するU-BootをRAM上のダウンロード領域(0x20000000)へダウンロードします。
=> tftp 2000000 u-boot.bin
Using FEC ETHERNET device
TFTP from server 192.168.0.26; our IP address is 192.168.0.120
Filename 'u-boot.bin'.
Load address: 0x2000000
Loading: ################################################
done
Bytes transferred = 241680 (3b010 hex)
そして、Flashのライトプロテクトを解除、該当のセクタを消去し、書き込みます。
=> protect off bank 1 => erase 1:252-252 => cp.b 2000000 fff00000 3b010
上記cpコマンドのサイズ(0x3b010)はtftpコマンドで実際に転送されたサイズを指定しています
Linuxカーネルを更新する場合も同様の手順になります。
=> tftp 2000000 uImage => protect off bank 1 => erase 1:241-250 => cp.b 2000000 FFC40000 <size>
FlashのライトプロテクトはU-Boot更新時に解除していますので続けて更新する場合は不要です。
デバイスファイルも同様の手順です。
=> tftp 2000000 mpc5121ads.dtb => protect off bank 1 => erase 1:251-251 => cp.b 2000000 FFEC0000 <size>
起動
ボード標準のU-Boot起動コマンドでFlash上のrootfsから起動するには以下の様にします。
=> run jffs2boot
カーネルコマンドラインを指定して起動する場合は以下の様にします
=> set bootargs console=ttyPSC0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2 mem=256M => bootm ffc40000 - ffec0000
bootmコマンドの引数はそれぞれ、
- ffc40000 : カーネルのアドレス
- - : initial ramdiskのアドレス(未使用なので省略を示す'-')
- ffec0000 : デバイスツリーのアドレス
と言う意味になります。
Flashを更新せずにRAMから起動する場合
デバッグ段階などで、Linuxカーネルやデバイスツリーを頻繁に更新するようなケースで、毎回Flashに書き込みをしていては非効率です。そこで両ファイルをFlashに書き込まずにRAMから起動する場合は以下の様にします。
=> tftp 2000000 uImage => tftp 3000000 ads5121.dtb => set bootargs console=ttyPSC0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2 mem=256M => bootm 2000000 - 3000000
または、Flash上のデバイスツリーファイルを使用する場合は、最後のbootmコマンドのデバイスツリーファイルのアドレスを、
=> bootm 2000000 - ffec0000
とします。
jffs2イメージをloopback mount
block2mtdドライバを使えば、jffs2イメージファイルをloopbackでmountできます。
erasesize 128K (131072) のrootfs.jffs2を/mntにmountする場合
mount:
# modprobe jffs2 # modprobe mtdblock # losetup /dev/loop0 rootfs.jffs2 # modprobe block2mtd block2mtd=/dev/loop0,131072 # mount -t jffs2 -o ro /dev/mtdblock0 /mnt
umount:
# umount /mnt # rmmod block2mtd # losetup -d /dev/loop0
seq_fileの使い方
Linuxカーネルが持つ、疑似ファイルの実装を補助する機構を紹介します。
カーネルからユーザ空間へデータを渡す手段としては、 /proc や /sys 以下に作成した疑似ファイルを使うことが多いでしょう。
渡したい値が単純な型の場合はカーネル組込みのヘルパ関数を使えば十分(sysfs 経由でモジュールパラメータにアクセス、sysfs 経由でモジュールパラメータにアクセス (2))ですが、 より複雑なデータを渡すならば:
- 疑似ファイルの内容となるテキスト量が大きい場合、カーネル空間に全体を保持たくない。
- データ構造を操作に時間がかかる場合、ユーザ空間から要求された部分だけを処理することが望ましい。
- 同時に複数の読み手が存在したり、読み出し途中でデータが変更される場合の排他が必要。
などの点を考慮するべきです。
本記事では、シーケンシャルな疑似ファイルを実装するために用意されている補助関数群について解説します。 カーネル内でも広く使われている機構なので、知っておくとコードを読む際にも役立つでしょう。
seq_file
VFSの層ではファイル中の位置はバイト単位で扱われます。固定長のバッファで扱える程度のデータ量ならよいのですが、 疑似ファイル内容を一括生成・保持できない場合、自力で出力済のバイト数管理やバイト単位でのシークを実装するのは手間がかかります。
fs/seq_file.cには出力したいデータが
- 通し番号が付けられる(配列や木などの)要素の集合として表現できる
- 要素の番号がわかれば、その要素の文字列表現が得られる
という条件を満たすとき、各要素へのアクセスをイテレータ操作として抽象化する仕組みが用意されています。
これを使うと、所定のアクセサを作成するだけで、VFSに登録する struct file_operations のメンバのほとんどをカーネルから提供される汎用関数でまかなうことができます。
使用例
サンプルとして、全ttyについてdebugfs上の疑似ファイル経由として termios状態を一覧するカーネルモジュールのソースtermios_dumper.cを用意しました。
個別のttyの状態取得ならtcgetattr()でも十分なのですが、システム上で 現在activeな全てのttyをリストするために、カーネル内部のデータ構造を直接読んでいます。
疑似ファイルの登録
seq_fileを使用する場合でも、ファイルシステムへの疑似ファイルの登録は通常のまま
- struct file_operationsを作成
- ファイルシステム毎の登録関数の呼び出し
という手順で行います。
ただし、VFSに渡すstruct file_operationsのうち、読みこみ可能な疑似ファイルを実装するために必要な
- open
- read
- llseek
- release
のうちread, llseek, releaseとしては、seq_fileで実装されているseq_read, seq_lseek, seq_releaseをそのまま使用できます。
.openに登録する関数では、
- open()されたファイルに対して、seq_open()を呼んでハンドラを登録。
- 必要ならリソースの確保/ロック
を行ないます。
サンプルでは登録先のファイルシステムをdebugfsとしたので、以下のようにしています。
static const struct file_operations fops = {
.open = termios_dumper_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
...
static int __init termios_dumper_init(void)
{
...
if(IS_ERR(fs_root = debugfs_create_dir(KBUILD_BASENAME, NULL)))
return PTR_ERR(fs_root);
if(IS_ERR(fs_file = debugfs_create_file("state", 0444, fs_root, NULL,
&fops)))
return PTR_ERR(fs_file);
この例では、特にリソースの初期化が必要ないため、.openに登録したtest_seqfile_openの実装はseq_open()の呼び出しだけです。
static int test_seqfile_open(struct inode *inode, struct file *file)
{
return seq_open(file, &seqfile_ops);
}
seq_operations
読み出し用のイテレータに必要な操作:
- 初期化
- 終了処理
- 「次」の要素に移動
- 値(文字列表現)の取得
は、
struct seq_operations {
void * (*start) (struct seq_file *m, loff_t *pos);
void (*stop) (struct seq_file *m, void *v);
void * (*next) (struct seq_file *m, void *v, loff_t *pos);
int (*show) (struct seq_file *m, void *v);
};
の各メンバとして登録されます。
start
seq_file用のファイルがopen()された時に呼ばれます。
- 使用するリソースの確保
- イテレータの初期化
を行ない、成功した場合には初期化済みのイテレータ、失敗した場合はNULLを返すことになるでしょう。
引数*posが0より大きい場合には、その位置を指すようにしたイテレータが返却されるべきです。
※ イテレータの更新処理は、(pos)ではなく、(*pos)を使います。
イテレータを進める処理は、next()からの処理でも必要となるため、共通化してもよいでしょう。サンプルでは update_iterator()としています。
next
seq_fileが、現在のイテレータからのデータを消費し終えた時、 現在のイテレータと次に指すべき位置を引数として呼ばれます。
読み手のread()に対応していると考えてよいでしょう。 ただし、カーネルによる先読み処理の対象となるため、read()の度にかならず呼ばれるわけではありあせん。 また、読み手が小量のデータしが要求していなくても、さらに先まで要求されることがあります。
指定位置を指すイテレータを用意して、それへのポインタを返します。 この場合返却したポインタは後述するshow()への引数として使われます。
処理に失敗した場合はNULLを返却します。このとき、続いてstop()が呼ばれますが、stop()の引数にはNULLしか渡らないため、リソースの開放にイテレータが必要なら、next()内で済ませておく必要があります。
stop
読み手に十分なテキストが渡ったか、next()からデータがもうないことを通知した(NULLを返した)後に呼びだされます。
リソースの開放を行なうべきですが、引数として渡されるポインタはNULLの場合があることに注意が必要です。
show
引数として渡されたイテレータから、読み手に渡す(疑似ファイルの内容となる)文字列を構築します。
構築にはseq_printf(), seq_puts()などのヘルパ関数を使用します。これらのヘルパは繰替えし呼ぶこともでき(追記されます)、最終的な長さは自動的に管理されるので、成功時には0を返せば適切に処理されます。
ただし、一度に構築できる文字列の全長はPAGE_SIZE程度なので、長くなりすぎる場合はイテレータの構造を変える必要があるでしょう。
サンプルコードでは、現在のイテレータが指しているtermiosの情報を、seq_printf()経由でgrepしやすい書式に変換しています。
サンプルtermios_dumper.cについての補足
サンプルとして使用したコードtermios_dumper.cでは、カーネルから公開されていはいttyドライバのリストの先頭要素のアドレスを知るために、struct tty_driverのmagic要素を用いて検索していますが、これは低い確率ですが(メモリ中の値が偶然TTY_MAGICと一致したり、get_current_tty()が呼べない場合に)失敗する可能性のある処理です。 確実を期すなら、カーネルの公開シンボルにtty coreのtty_driversを追加するか、専用のアクセサを追加するべきでしょう。
また、ttyサブシステム内の構造体を辿る処理はttyのmutexを取得した上で行うべきなのですが、
- tty_mutexを占有すると副作用が大きい(ロック中に新規にttyを開こうとした他のプロセス全てが停止するため)
- 改造して失敗したときにシステムをロックしてしまう
- 今回のサンプルでは読み出ししかおこなわないため、整合性が崩れても大きな問題にならない
ため、デモ目的では不要と判断してコメントアウトしてあります。 このため、疑似ファイルからの読出し途中でttyが増減すると、表示の一貫性が崩れることがあるでしょう。 同様の理由で、kobjectの参照数カウントについても省略しています。
Bishopバージョンアップのお知らせ
組込みLinux開発用CPUボードBishopに同梱されるソフトウェアのバージョンアップを実施いたします。
| 旧バージョン | 新バージョン | |
|---|---|---|
| U-Boot | 1.2.0-pylone5 (変更なし) | |
| Linuxカーネル | 2.6.22.1-pylone0 | 2.6.26.8-pylone0 |
| ルートファイルシステム | Debian GNU/Linux etch 4.0r3 | Debian GNU/Linux etch 4.0r5 |
2009年1月以降にご注文いただいた分から新バージョンにて出荷いたします。
既にご購入いただいたお客様へ
2008年12月までにご購入いただいたお客様につきましては、別途バージョンアップ手順をご案内いたします。
関連リンク
Bishopエミュレータ正式版リリース
組込みLinux開発用CPUボードBishopエミュレータの正式版をリリースしました。
ドキュメント
ダウンロード
| Bishopエミュレータ | |||
| qemu-bishop-0.9.1-pylone1.tar.bz2 ソースコード | 0.9.1-pylone1 | ダウンロード | 2.3MB |
| qemu-bishop-0.9.1-pylone1-setup.exe win32 installer | 0.9.1-pylone1 | ダウンロード | 1.7MB |
| qemu-bishop_0.9.1-pylone1-1_i386.deb deb | 0.9.1-pylone1-1 | ダウンロード | 461.0KB |
| qemu-bishop-0.9.1_pylone1-1.i386.rpm rpm | 0.9.1-pylone1-1 | ダウンロード | 462.8KB |
