Linux MIPSの TLBミス時に実行されるハンドラは
'arch/mips/mm/tlbex.c'で動的に生成されています。
なんでバイナリを逆アセンブルしてもわかりません。それが理解出来れば
何やってるかわかるんだろうけど、実際どんな命令列なんだって
ことでそれを調べる方法について書きます。
まず QEMUをインストールします。
% git clone git://git.qemu.org/qemu.git qemu_git % cd qemu_git % ./configure --target-list=i386-softmmu,mipsel-softmmu,arm-softmmu (ターゲット名は ./default-configs/以下の .makファイル名.) % make 2>&1 |tee qemu_build.log % sudo paco -D make install
ターゲットは必要そうなやつだけにしてください。指定しないと長いです。
今回は MIPS el(little endian)が必要です。
次に GDBをインストールします。バージョンは 7.2を使いました。
もしかすると Binutilsがいるかもしれません。同じ configure
オプションでビルドできますので、なにか言われたらそうしてみて
ください。
% cd gdb-7.2 % mkdir build_mips && cd build_mips % ../configure --target=mipsel-linux-elf \ --prefix=/home/syohei/local/mipsel_tools --disable-nls % make 2>&1 |tee build_mips.log % make install
QEMUで動く Linuxをここから
mipsel-test-0.2.tar.gzを取得し、展開します。そしてデバッグモードで
QEMUを動かします。(-Sオプションがそうです。)
% qemu-system-mipsel -S -s -kernel vmlinux-2.6.18-3-qemu -initrd initrd.gz -append "root=/dev/ram console=ttyS0 init=/bin/sh" \ -nographic -M 'mips' -m 128
そして GDBを起動して、QEMUに接続します。
カーネルイメージは同じものを指定してください。
% mipsel-linux-elf-gdb vmlinux-2.6.18-3-qemu (起動メッセージ) (gdb) target remote :1234 Remote debugging using :1234 0x8028b000 in kernel_entry ()
こんなメッセージが出たら大丈夫です。
MIPS32だと TLBミスが発生すると 0x80000000番地に飛ぶのでそこに
breakpointを仕掛けてプログラムを起動します。
(gdb) break *0x80000000 (gdb) c Continuing. Breakpoint 1, 0x80000000 in ?? ()
あとは PCから何命令か表示すればどんな命令列かわかります。
(gdb) x/20i $pc => 0x80000000: lui k1,0x802d 0x80000004: mfc0 k0,c0_badvaddr 0x80000008: lw k1,8216(k1) 0x8000000c: srl k0,k0,0x16 0x80000010: sll k0,k0,0x2 0x80000014: addu k1,k1,k0 0x80000018: mfc0 k0,c0_context 0x8000001c: lw k1,0(k1) 0x80000020: srl k0,k0,0x1 0x80000024: andi k0,k0,0xff8 0x80000028: addu k1,k1,k0 0x8000002c: lw k0,0(k1) 0x80000030: lw k1,4(k1) 0x80000034: srl k0,k0,0x6 0x80000038: mtc0 k0,c0_entrylo0 0x8000003c: srl k1,k1,0x6 0x80000040: mtc0 k1,c0_entrylo1 0x80000044: ehb 0x80000048: tlbwr 0x8000004c: eret
EntryLo0, EntryLo1に値を設定していたり、tlbwrをしていることから
間違いなさそうです。eretがあるんで、たぶんここで終わりでしょう。
その後は nopばっかりでした。
てかなんで Linuxはこの部分を動的生成しているんでしょうね。
アセンブリ言語で書いて、memcpyでぺろっとコピーすりゃいいじゃん
って思うんだけど、なにか理由があるんでしょう。
なおこれは古いカーネル(2.6.18)のものなんで、今は違う命令列かも
しれません。でも動的生成していることは変わりません。
NetBSDでは
一方 BSDではアセンブリ言語で書かれています。
以下は NetBSD 5.1の usr/src/sys/arch/mips/mips/mipsX_subr.Sの
コードです。該当アドレスへのコピーは同ディレクトリの mips_machdep.cの
mips3_vector_init関数で行っています。
VECTOR(MIPSX(TLBMiss), unknown) .set noat mfc0 k0, MIPS_COP_0_BAD_VADDR #00: k0=bad address lui k1, %hi(segbase) #01: k1=hi of segbase bltz k0, 4f #02: k0<0 -> 4f (kernel fault) srl k0, 20 #03: k0=seg offset (almost) lw k1, %lo(segbase)(k1) #04: k1=segment tab base andi k0, k0, 0xffc #05: k0=seg offset (mask 0x3) addu k1, k0, k1 #06: k1=seg entry address lw k1, 0(k1) #07: k1=seg entry mfc0 k0, MIPS_COP_0_BAD_VADDR #08: k0=bad address (again) beq k1, zero, 5f #09: ==0 -- no page table srl k0, 10 #0a: k0=VPN (aka va>>10) andi k0, k0, 0xff8 #0b: k0=page tab offset addu k1, k1, k0 #0c: k1=pte address lw k0, 0(k1) #0d: k0=lo0 pte lw k1, 4(k1) #0e: k1=lo1 pte sll k0, 2 #0f: chop top 2 bits (part 1a) srl k0, 2 #10: chop top 2 bits (part 1b) #ifdef MIPS3_5900 mtc0 k0, MIPS_COP_0_TLB_LO0 #11: lo0 is loaded sync.p #12: R5900 cop0 hazard sll k1, 2 #13: chop top 2 bits (part 2a) srl k1, 2 #14: chop top 2 bits (part 2b) mtc0 k1, MIPS_COP_0_TLB_LO1 #15: lo1 is loaded sync.p #16: R5900 cop0 hazard #else /* MIPS3_5900 */ mtc0 k0, MIPS_COP_0_TLB_LO0 #11: lo0 is loaded sll k1, 2 #12: chop top 2 bits (part 2a) srl k1, 2 #13: chop top 2 bits (part 2b) mtc0 k1, MIPS_COP_0_TLB_LO1 #14: lo1 is loaded nop #15: standard nop nop #16: extra nop for QED5230 #endif /* MIPS3_5900 */ tlbwr #17: write to tlb nop #18: standard nop nop #19: needed by R4000/4400 nop #1a: needed by R4000/4400 eret #1b: return from exception 4: j _C_LABEL(MIPSX(TLBMissException)) #1c: kernel exception nop #1d: branch delay slot 5: j slowfault #1e: no page table present nop #1f: branch delay slot .set at _VECTOR_END(MIPSX(TLBMiss))
ページテーブルの構成も違うんだろうけど、NetBSDのコードは TLBミスハンドラで
ページが見つからなかったら、フォールトハンドラのコードへ飛ぶみたい。
Linuxだとページがあろうがなかろうが、ページを突っ込んで、本当になければ
TLB invalid例外を発生させてそこでページテーブルの更新しているのかなと
思われます。
間違えていたらごめんなさい。