2013年1月30日水曜日

DM355のインストールディスクを作る 後編

前回は、DM355のブート処理を概観し、SDカード用のブートローダ (SD-UBL) を試してエラーを起こすところまで扱いました。今回はSD-UBLを分析・修正して、実際にインストールディスクを作ってみたいと思います。

メッセージを分析

SD-UBLが出すメッセージはけっこう冗長なので保存して比較してみると、ブートモードとインストールモードではカーネル (Linux) とRAMディスクのロード先が入れ替わっていることが分かります。関係するメッセージだけ引用します:

ブート時のメッセージ:
* Loading kernel
sdcard_read sdc_src_addr=0x00081000 dst=0x82000000 len=0x00200000
* Loading ramdisk
sdcard_read sdc_src_addr=0x00281000 dst=0x80700000 len=0x00400000
インストール時のメッセージ:
* Flashing kernel
sdcard_read sdc_src_addr=0x00081000 dst=0x80700000 len=0x00200000
* Flashing Root FS
sdcard_read sdc_src_addr=0x00281000 dst=0x82000000 len=0x00400000
SDカードにアクセスできないU-Bootを使っていますから、ロード先が入れ替わっては起動するはずがありません。しかし、ひとまずU-Bootのパラメータをデフォルトから変更すれば起動しそうです。実際にU-Bootの自動起動を中止して起動スクリプトを書き直してやると起動しました。

ただし、このままのメモリ配置では9MiBより大きなRAMディスクをロードできません。やはりSD-UBLの修正が必要です。それでハックの方針は以下のようになります:

Linuxとramdiskのロード先アドレスを変更する
ロードサイズを変更する
なお、インストールモードのフラッシュ書き換え機能は、ブートメッセージによれば、TIがリリースしているユーティリティをベースにしているらしいので使いません。そのTIのユーティリティはNANDフラッシュの書き込みエラー処理とかしてないっぽいので、信用できないからです。

SD-UBLを解析する

まずはディスクイメージからSD-UBLを探します。RBLの仕様はTIが公開しているDM355のARM subsystemのデータシートに書かれていますから、RBLになったつもりでディスクイメージを見ていきます……すぐに見つかりますね。ダンプしてみると、第1セクタにUBLディスクリプタが書かれています:

00000200: 00 ed ac a1 00 01 00 00 2e 00 00 00 09 00 00 00
左から順に、マジックナンバー0xa1aced00、エントリポイントが0×100、サイズが0x2eセクタ、先頭は第9セクタ、という意味です。なおUBLはメモリ空間の0×0020にロードされるので解析には注意が必要です。

位置が分かったのでSD-UBLを切り出します:

dd if=dm355_boot.sdcard of=sd-ubl.bin bs=512 skip=9 count=$((0x2e))
逆アセンブルします:

arm_v5t_le-objdump -b binary -m arm -D sd-ubl.bin > sd-ubl.s
逆アセンブルしたソースを検索してみると、ロード元のアドレス0×41000とか0×81000を引数にして同じサブルーチンが3回ほど続けて呼ばれていることが分かります。関係する部分を引用します:

2fd8: e59f3064 ldr r3, [pc, #100] ; 0x3044
2fdc: e59f4064 ldr r4, [pc, #100] ; 0x3048
2fe0: e5932000 ldr r2, [r3] ; 0x15aa4
2fe4: e1a01004 mov r1, r4
2fe8: e1a02482 mov r2, r2, lsl #9
2fec: e3a00a41 mov r0, #266240 ; 0x41000
2ff0: ebfffe76 bl 0x29d0

3044: 00015aa4 andeq r5, r1, r4, lsr #21
3048: 81080000 tsthi r8, r0
304c: 00015594 muleq r1, r4, r5

5a84: 0000012c andeq r0, r0, ip, lsr #2
ARMのgccの関数呼び出し規約ではr0からr3が引数ですので、そっちのレジスタも見てみると、ロード先とサイズも引数として渡されているらしいことが分かります (簡単に書いてますが劇的な場面ですよ)。実にExcellentなコードですね。

0x15aa4という変なアドレスにアクセスしているので解説しておきましょう。TCMは0×00000と0×10000の両方からアクセスできるようになっているので、アドレス0x15aa4は0x5aa4と同じです。さらに、UBLのロードされるアドレスは0×00020ですから、0x15aa4へのアクセスは 0x15aa4 = 0×10000 + 0×20 + 0x5a84 と分解することができ、ソースの5a84:の部分へのアクセスになるわけです。

同様に解析するとU-Boot、Linux、ramdiskのロードがほぼ同じように書かれているらしいことが分かります。ロードする長さだけは変数としてメモリ上に置いてあります (グローバル変数なのでしょう……なぜだろう)。

バイナリパッチ

結局、具体的には以下のようにバイナリエディタでハックします (Emacsでは M-x hexl-find-file):

Linuxとramdiskのロード先はハードコードされているので入れ替える:
3000: e3a01482 を e59f1054 に変更
301c: e59f1038 を e3a01482 に変更
グローバル(?)変数になっているそれぞれのイメージサイズを変更する:
5a84: U-Bootのセクタ数
5a88 ramdiskのバイト数
5a8c Linuxのバイト数
淡々と結果だけ書いてしまいましたが、ARM命令は32bit固定長なのでバイナリパッチが簡単なのです。今回は値を入れ替えたりそのままメモリ上に変数として置いてある値を書き換えるだけなのでコードも増えませんし、PC相対のディスプレースメントだけちょっと計算すればお終いです。

あとはU-Bootの環境変数を書き換えておいて、SDカードに書き込めばインストールディスクのできあがりです。好きなインストーラが起動するように仕込みましょう:

dd of=/dev/sdc bs=512 seek=9 if=sd-ubl.bin
dd of=/dev/sdc bs=512 seek=520 if=uboot.bin
dd of=/dev/sdc bs=512 seek=1032 if=uImage #linux
dd of=/dev/sdc bs=512 seek=5128 if=fs.bin #ramdisk
おしまい

SD-UBLがバグっているので多少バイナリパッチをしましたが、ARMのバイナリは割とハックしやすいと思います。ブートローダ程度のものなら皆さんもハックしてみてはいかがでしょうか?

0 件のコメント:

コメントを投稿