MIPS(3)
レバ刺が禁止になっちゃって、そろそろ禁断症状が出てるかな? そういう人は、夏休みを利用 して、隣の国へでもレバ刺食べ放題ツアーに行けばいいと思うぞ。
でも、お上は昔っから、禁止禁止何でも禁止の方向へ走ってませんかね。レバ刺ぐらい、事故判断 で食べてもいいと思うんだけどな。そのうちにヤミレバとかが出てくるに違いない。
禁止と言えば、近くの公園が有るんだけど、禁止禁止の看板が立ってるな。ボール遊びしては いけません。ペットは連れてこないでください、等々。一体、公園で何をしろと? 爺婆が年に 一度集まって、桜の花見ぐらいですかね? 息が詰まるような世の中だ事。
最近、川の学校って本を読んだ。副題が、川がき養成講座なんて書いてあるんで、そそっかしい おいらは、川でも牡蠣が取れるんかと思っちゃったぞ。牡蠣じゃなくて餓鬼ね。
川を遊び場にして、水泳をやるも好し、滝つぼ目掛けて飛び込みやるも好し、魚を釣っても、 捕まえても好し、カヌーで遊んでも好し、自分の好きなように遊べって言うのを提唱する学校。
その本にも、禁止禁止が載ってた。川は危ないから近寄ってはいけない、ナイフを持つのは危ない、 生き物を殺してはいけない、等々。いけないずくしですよ。これじゃ子供は萎縮しちゃうよ。
で、川の学校の主催に繋がったんだとか。もう12年も続けていて、毎度入学希望者が絶えないそう。 フィールドは、四国の吉野川。綺麗で、水が温かく、川遊びには最適とか。 いいね。
おいらも昔、よく川で遊んだものだ。自転車のスポークを改造して、フォークみたいなヤスを 作って、かじかとかよく取ったなあ。焼いて食べると最高。また、やりたいな。
再びpmax
前回cobalt箱で外からファイルを輸入する方法を見た。今度はpmaxでと思ってやってみたら、DISKが IDE接続じゃなくてSCSI接続なのね。だから、こんな風にやれば良い。
[sakae@secd ~/pmax]$ cat xboot #!/bin/sh gxemul -x -e 3max -d nbsd_pmax.img -d sd1c.tgz
こんな風な起動スクリプトを用意して
[sakae@secd ~/pmax]$ ./xboot GXemul 0.6.0 Copyright (C) 2003-2010 Anders Gavare Read the source code and/or documentation for other Copyright messages. Simple setup... net: simulated network: 10.0.0.0/8 (max outgoing: TCP=100, UDP=100) simulated gateway+nameserver: 10.0.0.254 (60:50:40:30:20:10) simulated nameserver uses real nameserver 192.168.136.2 machine: memory: 64 MB cpu0: R3000 (I+D = 4+4 KB) machine: DECstation 5000/200 (3MAX, KN02) (25.00 MHz) bootstring(+bootarg): boot 5/rz0/ -a diskimage: nbsd_pmax.img SCSI DISK id 0, read/write, 2929 MB (6000003 sectors) diskimage: sd1c.tgz SCSI DISK id 1, read/write, 35 MB (72930 sectors) DEC boot: loadaddr=0xa0700000, pc=0xa0700000: 13 blocks cpu0: starting at 0xa0700000
後は、tarの引数に、/dev/sd1c を指定するだけ。テープ・アーカイブのtarじゃなくて、dar(の猪木) だー。
あれ? ブートする時のPCアドレスが、cobaltのそれとは全く違うなぁ! これって使ってる石の 違いなのかな? ここらで、R3000のちゃんとした資料を用意しておくか。
MIPSな会社の資料室 とか はじめて読む MIPS なんてのが、いいかな。MIPSって、RISCな石なんで、アセンブラからして、MACROを使って便利な 命令を定義してんのね。ソースを読む時は要注意だな。
demos
gxemulのソースツリーを眺めていたら、demosフォルダーが用意されてて、Helloって印字させましょ が載ってた。どんな風にやるかと思ってREADMEを眺めると
MIPS (64-bit) ------------- mips64-unknown-elf-gcc -I../../src/include/testmachine -g -DMIPS hello.c -mips4-mabi=64 -c -o hello_mips.o mips64-unknown-elf-ld -Ttext 0xa800000000030000 -e f hello_mips.o -o hello_mips --oformat=elf64-bigmips file hello_mips ../../gxemul -E testmips hello_mips MIPS (32-bit) ------------- mips64-unknown-elf-gcc -I../../src/include/testmachine -g -DMIPS hello.c -mips1-mabi=32 -c -o hello_mips32.o mips64-unknown-elf-ld -Ttext 0x80030000 -e f hello_mips32.o -o hello_mips32 file hello_mips32 ../../gxemul -E testmips -C R3000 hello_mips32
残念ながらFreeBSD上にMIPS用のコンパイラが無いので、試してみるわけにはいかないな。でも、 融合のやり方がわかっただけでも好しとするか。 hello.cも見ておくと吉。
実機で実験
ちょっと不満が募ってしまったので、実機であれこれやってみるか。gxemulのmanを見ると、特定の 番地でbreakさせる、-p オプションが用意されてるし。。。 その前に、netbsdの何処で止めたりするかだな。ちょっと下調べ。
pmax# file /netbsd /netbsd: ELF 32-bit LSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, for NetBSD 5.0, not stripped
debug用のシンボルが幸運な事に消されていなかった。
pmax# readelf -a /netbsd ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: MIPS R3000 Version: 0x1 Entry point address: 0x80030000 Start of program headers: 52 (bytes into file) Start of section headers: 3885140 (bytes into file) Flags: 0x1001, noreorder, o32, mips1 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 2 Size of section headers: 40 (bytes) Number of section headers: 30 Section header string table index: 27 :
ふーん、demosのそれとスタート番地が一緒だな。ついでに、どんなシンボルが有るかも 見ておく。
pmax# nm /netbsd | head 800cf854 t AllocRaidReconDesc 80339b78 r C.34.12317 800b3ae0 t CheckCvscanState 800cebf4 t CheckForNewMinHeadSep 800ce6d0 t ComputePSDiskOffsets 80030598 T CopyFromBuffer 80030570 T CopyToBuffer 800bff64 t DAGExecutionThread 80333d78 r DefaultStates 80030478 t FPReturn
沢山ありそうなので、個数だけでも。
pmax# nm /netbsd | fgrep ' T ' | wc 6029 18087 157740 pmax# nm /netbsd | fgrep ' t ' | wc 2539 7617 66179
大文字のTが付いているやつは、グローバルシンボルで、小文字の方はローカルなシンボルか。 全部合せて、8000を超えてる。全部が全部、関数じゃないだろうけど。取り合えず ソース全部と、nmから抜き出した、シンボルテーブルを用意した。(シンボルテーブルは、Kern_symって 名前で、FreeBSD側へ持ってきておいた)
で、gxemulでbreakpointを設定しようとすると、どうもサブコマンドを指定しないといけない みたいだ。マニュアルを探してみたけど書いて無いので、ソース嫁となります。debugger_cmd.cc に答えが書いてありました。
if (cmd_line[0] == '\0') { printf("syntax: breakpoint subcmd [args...]\n"); printf("Available subcmds (and args) are:\n"); printf(" add addr add a breakpoint for address addr\n"); printf(" delete x delete breakpoint nr x\n"); printf(" show show current breakpoints\n"); return;
何も引数を与えないと、(怒ったように)指導してくれたって事ね。そういう、ユーザーインターフェースも 有りか。これは盲点だったわい。
気分はKernel debugger
準備が整ったんで、NetBSDの起動から見て行く。まずは、エントリーポイントにBPを置いて起動。
[sakae@secd ~/pmax]$ gxemul -e 3max -d nbsd_pmax.img -p 0x80030000 NetBSD/pmax 5.0.2 FFS Primary Bootstrap NetBSD/pmax 5.0.2 Secondary Bootstrap, Revision 1.5 (builds@b7.netbsd.org, Sun Feb 7 01:50:00 UTC 2010) Boot: 5/rz0/ Loading: 5/rz0/netbsd.pmax open 5/rz0/netbsd.pmax: No such file or directory Loading: 5/rz0/netbsd 3440064+316192 [209392+200661]=0x3f978c Starting at 0x80030000 80030000: 80710370 lb s1,880(v1) [0x8001ff70] BREAKPOINT: pc = 0x80030000 (The instruction has not yet executed.) GXemul> step 80030000: 80710370 lb s1,880(v1) [0x8001ff70]
kernelをロードするまでは、pmax付属のROMの役目なんだな。ロードが終わって、カーネルの 実行が始まりました。
GXemul> unass 80030004: 3c038003 lui v1,0x8003 80030008: 24630000 addiu v1,v1,0 8003000c: 007d102a slt v0,v1,sp 80030010: 14400005 bne zr,v0,0x80030028 80030014: 2462ffe8 addiu v0,v1,-24 80030018: 007d1023 subu v0,v1,sp 8003001c: 28421000 slti v0,v0,4096 80030020: 10400002 beq zr,v0,0x8003002c 80030024: 2462ffe8 addiu v0,v1,-24 80030028: 0040e821 move sp,v0 8003002c: 3c1c8038 lui gp,0x8038 80030030: 279cfda0 addiu gp,gp,-608 80030034: 40086000 mfc0 t0,status 80030038: 3c012000 lui at,0x2000 8003003c: 01014025 or t0,t0,at 80030040: 40886000 mtc0 t0,status 80030044: 00000000 nop 80030048: 00000000 nop 8003004c: 00000000 nop 80030050: 00000000 nop GXemul> reg cpu0: pc = 80030004 < no symbol > cpu0: hi = 00000008 lo = 00000000 cpu0: at = 30464354 v0 = b007babe v1 = 8001fc00 cpu0: a0 = 00000002 a1 = a0007f84 a2 = 30464354 a3 = bfc04000 cpu0: t0 = 8002ffe8 t1 = 80030000 t2 = 00000ec9 t3 = 00000000 cpu0: t4 = 00418ec9 t5 = 00000000 t6 = 00418000 t7 = 00000000 cpu0: s0 = 80030000 s1 = 00000000 s2 = 8070fbc4 s3 = a0007f84 cpu0: s4 = 8071bd4c s5 = 00000002 s6 = 00000000 s7 = 00000000 cpu0: t8 = 00000000 t9 = bfc08060 k0 = 00000000 k1 = 00000000 cpu0: gp = 8071be70 sp = 8002ffe8 fp = 00000000 ra = 80710c08 GXemul> reg ,c cpu0: index=00000000 random=00000000 entrylo0=00000000 entrylo1=00000000 cpu0: context=00000000 pagemask=00001fff wired=00000000 reserv7=00000000 cpu0: badvaddr=00000000 count=00000000 entryhi=00000000 compare=00000000 cpu0: status=30080000 cause=00000000 epc=00000000 prid=00000220
まだ、コプロの初期化も何も行われていないっぽい。これから、mainが動く為の準備にいそしむ のか。あれ、kernelにmainなんて有るのか? こんな時にはKern_symを見ればいいんだな。
[sakae@secd ~/pmax]$ grep main Kern_sym 803137c8 T compat_09_sys_getdomainname 80313778 T compat_09_sys_setdomainname 8024ad50 T domain_attach 8024bec0 T domaininit 80289540 T if_attachdomain 8028730c T if_attachdomain1 801d6ec8 T main 8024ae50 T pffinddomain 802279fc T specificdata_domain_create 80227570 T specificdata_domain_delete
ビンゴ。有ったね。じゃ、mainに飛んでくる事を期待して、網を張ってから続きを実行してみる。
GXemul> br add 0x801d6ec8 1: 0x801d6ec8 (0x801d6ec8) GXemul> cont segment 0 start 00000000 size 04000000 801d6ec8: 27bdffb0 addiu sp,sp,-80 BREAKPOINT: pc = 0x801d6ec8 (The instruction has not yet executed.) GXemul> step 801d6ec8: 27bdffb0 addiu sp,sp,-80 GXemul> unass 80030054: 00000000 nop 80030058: 00000000 nop 8003005c: 40087800 mfc0 t0,prid 80030060: 00000000 nop 80030064: 00000000 nop 80030068: 00000000 nop 8003006c: 00000000 nop 80030070: 44490000 cfc1 t1,r0 80030074: af888014 sw t0,-32748(gp) 80030078: af898018 sw t1,-32744(gp) 8003007c: 3c178037 lui s7,0x8037 80030080: 26f72440 addiu s7,s7,9280 80030084: 3c08803c lui t0,0x803c 80030088: 25084568 addiu t0,t0,17768 8003008c: ad170110 sw s7,272(t0) 80030090: aee8000c sw t0,12(s7) 80030094: 0c0c2d8e jal 0x8030b638 80030098: 00000000 nop 8003009c: 3c1d803c lui sp,0x803c 800300a0: 8fbd3a28 lw sp,14888(sp)
おお、飛んできたぞ。この時点では、コプロとか、tlbは初期化されていない。JALで旅に出て いろいろやってくのね。ああ、jalってのは、MIPS語で、Jump and Linkの略。平たく言うと、 サブルーチンコールだ。RISCの慣わしとして、戻り番地は、レジスター番号31、俗称で言うとraって いうレジスターに保存されるんだった。
[sakae@secd ~/pmax]$ grep 8030b638 Kern_sym 8030b638 T mach_init
ちなみに、最初の旅は、マシンのイニシャライズでした。続いて実行すると
GXemul> c Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc. All rights reserved. Copyright (c) 1982, 1986, 1989, 1991, 1993 The Regents of the University of California. All rights reserved. : Starting virecover. Starting local daemons:. Updating motd. Thu Jul 1 09:04:52 JST 2010 NetBSD/pmax (pmax) (console) login: sakae Password: NetBSD 5.0.2 (GENERIC) #0: Sun Feb 7 02:49:27 UTC 2010 Welcome to NetBSD! pmax$
ここに来るまでに、裏ではどれぐらいのマシンコードが実行されているんだろう。思いを馳せて みるのも悪くないな。
pmax$ cd src/sys pmax$ find . -name '*c' | xargs grep mach_init : ./arch/pmax/pmax/machdep.c:mach_init(argc, argv, code, cv, bim, bip) :
ちょっとmachdep.cを覗いてみると、tlbとかの準備やらCPUの準備やらをやってるっぽい。こういう 細かい事の流れは、ユニマガのFreeBSDのbootを見るあたりが参考になるかな。
pmax$ GXemul> reg ,c cpu0: index=00000100 random=00001500 entrylo0=03f90700 entrylo1=00000000 cpu0: context=001191e4 pagemask=00001fff wired=00000000 reserv7=00000000 cpu0: badvaddr=c6479928 count=00000000 entryhi=00000080 compare=00000000 cpu0: status=0008ff01 cause=00000800 epc=80205b50 prid=00000220 GXemul> tlbdump cpu0: (index=0x1 random=0x15) 0: vaddr=0xc5bae000 (global), paddr=0x03f91000 D 1: vaddr=0xc5baf000 (global), paddr=0x03f90000 D 2: (invalid) : 62: vaddr=0xc54d1000 (global), paddr=0x02ff1000 D 63: vaddr=0xc531a000 (global), paddr=0x03194000 D GXemul> cont pmax$
CTRL-Cでマシンを止め、中の状態を確認してみた。ちゃんとtlbも設定されてるね。
systemcallを追う
そんじゃ、システムコールを追ってみるか。ユーザーが発するコマンド(例えばls等)が、 システムコールとなってkernelに届くんだった。
lsってやった時、どんなシステムコールが発せられるかは、trussとかstraceとかで追跡出来る けど、生憎NetBSDには、今の時点で入っていない。はてどうするかで、man -k trace したよ。 そしたら、kteraceってのが見つかった。
pmax$ ktrace -tc ls Simple ktrace.out src pmax$ kdump | more : 285 1 ls CALL lseek(5,0,0,0,1,2) 285 1 ls RET lseek 512/0x200 :
トレースフラグに、システムコールだけを記録するように指示して、lsを実行。結果は、ktrace.out に出て来る。こやつはバイナリーなんで、専用ツールのkdumpを使って覗いてやる。へー、lsって、 lseekって言うシステムコールを使ってるんだ。
例によって、lseekのアドレスを調べておく。
[sakae@secd ~/pmax]$ grep sys_lseek Kern_sym 8031400c T compat_43_sys_lseek 8026d5f0 T sys_lseek
compatってのが出てきたけど、これはNetBSDがいろいろなOS(Linuxとか)のふりをするための 仕掛けだ。NetBSDネイティブな方は、sys_ が、頭に付く。
GXemul> br show 0: 0x80030000 (0x80030000) 1: 0x801d6ec8 (0x801d6ec8) GXemul> br delete 1 GXemul> br add 0x8026d5f0 1: 0x8026d5f0 (0x8026d5f0) GXemul> cont pmax$ ls 8026d5f0: 27bdff48 addiu sp,sp,-184 BREAKPOINT: pc = 0x8026d5f0 (The instruction has not yet executed.) GXemul> step 8026d5f0: 27bdff48 addiu sp,sp,-184 GXemul> unass 800300f4: acdd00a4 sw sp,164(a2) 800300f8: acde00a8 sw fp,168(a2) 800300fc: acdf00ac sw ra,172(a2) 80030100: acc800b0 sw t0,176(a2) 80030104: 0080b021 move s6,a0 80030108: 00a0b821 move s7,a1 8003010c: 27bdffe8 addiu sp,sp,-24 80030110: afbf0014 sw ra,20(sp) 80030114: 3c0a803c lui t2,0x803c 80030118: 8d4a394c lw t2,14668(t2) 8003011c: 02e02021 move a0,s7 80030120: 0140f809 jalr ra,t2 80030124: 00000000 nop 80030128: 3c01803c lui at,0x803c 8003012c: ac374678 sw s7,18040(at) 80030130: 8ee90130 lw t1,304(s7) 80030134: 8ee40018 lw a0,24(s7) 80030138: 8d2300b0 lw v1,176(t1) 8003013c: 24881f60 addiu t0,a0,8032 80030140: 10600009 beq zr,v1,0x80030168 GXemul> cont 8026d5f0: 27bdff48 addiu sp,sp,-184 BREAKPOINT: pc = 0x8026d5f0 (The instruction has not yet executed.) GXemul> c Simple ktrace.out src
なかなか面白いな。