MicroBlaze linux xmd でデバッグ tips
kernel の本家にマージされる前の MicroBlaze linux を作るのはけっこうステップが多くて面倒くさかった記憶があるが、Xilinx の git サーバで公開されている kernel と dts を組み合わせると驚くほど手間をかけずにとりあえず MicroBlaze で linux が起動するようになった。気がする。
以前の kernel では platform_device というデータを arch/ 以下に作ってやり、レジスタのアドレスや割り込み番号などを格納して、それをドライバから使っていたが、dts を使うと of_platform という別のインターフェース(というかデータベースみたいなもの)を使ってリソースを取得するよう、ドライバの作りが変わってきている。ようだ。
しかし、適当なドライバを書いていると kernel panic が起きることは起きるので、xmd を使ったデバッグの tips をメモしておく。
デバッグには、MicroBlaze の仕様書と、xmd の使い方マニュアルを用意しておくと良い。
問題は I2C を使うドライバを書いてる時におきた。I2C コアのレジスタ空間を ioremap() してレジスタに値を書こうとしたところでエラーが起きた、という問題だ。
kernel 起動時のログ:
[logi_touch_init()]: logi_touch test loaded logi_touch_driver registed [logi_touch_probe()]: map 0x50006000 -> 0xC201C000 Data bus error exception in kernel mode. Oops: bus exception, sig: 7 Registers dump: mode=11021D10 r1=00000000, r2=00000000, r3=00000001, r4=FFFFFFFF r5=C201C000, r6=C01DE294, r7=00000001, r8=00000A02 r9=C025DC50, r10=00000A02, r11=00000008, r12=FFFFFFFB r13=00000000, r14=C0007564, r15=C0254BCC, r16=00000000 r17=C0254BDC, r18=00000000, r19=C024194C, r20=00000000 r21=00000000, r22=C201C000, r23=C0241908, r24=C01711A4 r25=C0241954, r26=00000000, r27=C02402F4, r28=00000000 r29=00000000, r30=00000000, r31=C101FC60, rPC=C0254BDC msr=000046A2, ear=FFFFFFED, esr=C0241908, fsr=C01711A4 Kernel panic - not syncing: Attempted to kill init!
起動ログにレジスタの値がダンプされているが、いまいちアテにならないことが後で分かったので、自分で直接調べる方法を書く。
まずは xmd から break point を設定して、エラーの起きる直前で MicroBlaze を止める。そのために、objdump で vmlinux をディスアセンブルし、該当するコードをアセンブラで見て、break point を設定したい番地を特定する必要がある。
hal@heavens-door:~/microBlaze/new_lc3/linux-2.6-xlnx $ mb-linux-objdump -d vmlinux > /tmp/vm
問題の起きる関数は printk() でデバッグメッセージを入れるなどして、だいたい目星がついていたので、該当の関数のアセンブラをエディタで確認してみる:
c0254b8c: c0254b8c: b0005000 imm 20480 c0254b90: 30a06000 addik r5, r0, 24576 c0254b94: 3021ffe0 addik r1, r1, -32 c0254b98: f9e10000 swi r15, r1, 0 c0254b9c: fac1001c swi r22, r1, 28 c0254ba0: b000ffdb imm -37 c0254ba4: b9f42940 brlid r15, 10560 // c00074e4 c0254ba8: 30c00004 addik r6, r0, 4 c0254bac: 12c30000 addk r22, r3, r0 c0254bb0: b000c01e imm -16354 c0254bb4: 30c0b2dc addik r6, r0, -19748 c0254bb8: b0005000 imm 20480 c0254bbc: 30e06000 addik r7, r0, 24576 c0254bc0: b000c01e imm -16354 c0254bc4: 30a0b25c addik r5, r0, -19876 c0254bc8: b000ffdb imm -37 c0254bcc: b9f4e894 brlid r15, -5996 // c0013460 c0254bd0: 11030000 addk r8, r3, r0 c0254bd4: 10b60000 addk r5, r22, r0 c0254bd8: 30600001 addik r3, r0, 1 c0254bdc: f8760000 swi r3, r22, 0 c0254be0: b000ffdb imm -37 c0254be4: b9f428ac brlid r15, 10412 // c0007490
0xc0254bdc 番地の swi 命令がたぶん、問題を起こしている原因のコードだと思われる。
xmd を起動し、break point を設定する。break point はソフトウェア bp ではなく、ハードウェア bp にしなければならない(けっこう忘れがちで、ハマる)。
$ xmd XMD % connect mb mdm XMD % stop XMD % bps 0xc0254bd8 hw XMD% bpl 0: HW BP: Address = 0xc0254bd8
bpl で設定した break point を確認出来る。ちなみに break point を消すには bpr all で良い。
break point を設定したので linux を起動してみる。
XMD % dow kernel XMD% run RUNNING> XMD% XMD%
break したので、レジスタを見てみる。rrd と srrd でレジスタをダンプすることが出来る。
XMD% rrd r0: 00000000 r8: 00000a02 r16: 00000000 r24: c01711a4 r1: c1021dc0 r9: c025dc50 r17: c016cb6c r25: c0241954 r2: 00000000 r10: 00000a02 r18: 00000000 r26: 00000000 r3: 00000037 r11: 00000008 r19: c024194c r27: c02402f4 r4: ffffffff r12: fffffffb r20: 00000000 r28: 00000000 r5: c201c000 r13: 00000000 r21: 00000000 r29: 00000000 r6: c01de294 r14: c0013c2c r22: c201c000 r30: 00000000 r7: 00000001 r15: c0254bcc r23: c0241908 r31: c101fc60 pc: c0254bd8 msr: 000065a2 XMD% srrd pc: c0254bd8 msr: 000065a2 ear: c2008008 esr: 00000000 .... ....
次に stp でステップ実行して、問題を起こす。
XMD% stp C0254BDC: F8760000 swi r3 , r22, 0 XMD% srrd pc: c0254bdc msr: 000065a2 ear: c2008008 esr: 00000000 XMD% stp 20: B0001000 imm 4096 24: B8080FB0 brai 4016 XMD% srrd pc: 00000020 msr: 000046a2 ear: c201c000 esr: 00000c72
これで問題が起きた。pc が変わったのと、esr に値が書かれたことに注目する。
まず、esr ( 例外ステータスレジスタ)を確認する。値は 0x00000c72 ( MicroBlaze は big endian なので注意)なので、MicroBlaze の仕様書を見ながら例外の原因 EC と、例外ステータス ESS を突き止める:
ESS ( 例外ステータス ) => 110001
EC ( 例外の原因 ) => 10010
仕様書によると、例外の原因は データ TLB ミスによる例外であることがわかった。
TLB は変換 Look-Aside バッファの略らしく、データ TLB ミスとは、ようするに仮想アドレスを変換するための、変換用のテーブル(エントリというらしい)が無い、ということのようだ。
とりあえず xmd でのデバッグは、ここまで。
TLB を作るためのデータが多分カーネルに足りないか、与えてるつもりでもうまく与えられていないのだと思うが、あとは linux の話だ。
2/3 追記
TLB ミスによる例外が起きるのは正しいことが分かった。kernel はこのあと、MicroBlaze の UTLB に I2C レジスタ空間へアクセスするための情報を登録し、MicroBlaze は再度、例外の起きた swi 命令まで PC を戻して、swi 命令を実行する。
問題は UTLB に情報を登録したにも関わらず、2回目の swi 命令でバスエラーが起きることである。
この問題は暇な時にでも改めて調査しようと思う。