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のちゃんとした資料を用意しておくか。

R3000-manual

MIPS Instruction Reference

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

なかなか面白いな。