6.828: Operating System Engineering (71)

mem.pdf の以下の課題に着手。

Exercises
1. Set a breakpoint at swtch. Single step with gdb’s stepi through the ret to forkret, then use gdb’s finish to proceed to trapret, then stepi until you get to initcode at virtual address zero.
2. Look at real operating systems to see how they size memory.

とりあえず 1 から

make qemu-gdb して swtch で break すれば良いのかな。とは言え、何を確認すれば良いのやら。以下かなぁ。

  • swtch 手続きを経由してまず forkret 手続きに jmp していること
  • forkret が終了したら trapret 手続きに jmp していること
  • trapret から iret したら initcode の start に制御が移っていること
  • initcode から init が exec されていること

なのかどうなのか。
まず一つめ

(gdb) n
=> 0x103dcf <swtch+19>: pop    %ebp
swtch () at swtch.S:27
27        popl %ebp
(gdb) n
=> 0x103dd0 <swtch+20>: ret    
swtch () at swtch.S:28
28        ret
(gdb) n
=> 0x1030b0 <forkret>:  push   %ebp
forkret () at proc.c:353
353     {
(gdb)

を、forkret に制御が移りました。次は、って cpu を二つ認識してるのでトレイス微妙。cpu 一つで云々なオプションは何だ。こうすりゃ良いのかな。

$ CPUS=1 make qemu-gdb

で再度実行してるのですが、forkret から戻ったら swtch に制御が飛ぶなぁ。n じゃなくて si で進めないと駄目なのかな。

リトライ

forkret からひたすら si で進めてたら trapret に制御が移ったことが確認できました。

(gdb) si
=> 0x1030c2 <forkret+18>:       leave  
forkret () at proc.c:358
358     }
(gdb) si
=> 0x1030c3 <forkret+19>:       ret    
0x001030c3 in forkret () at proc.c:358
358     }
(gdb) si
=> 0x104cf0 <trapret>:  popa   
trapret () at trapasm.S:31
31        popal
(gdb) 

ここも我慢して si で進めてみます。で、iret のあたりが以下。

(gdb) si
=> 0x104cf7 <trapret+7>:        add    $0x8,%esp
36        addl $0x8, %esp  # trapno and errcode
(gdb) si
=> 0x104cfa <trapret+10>:       iret   
37        iret
(gdb) si
=> 0x0: push   $0x24
0x00000000 in ?? ()
(gdb) 

ふむ。0x0 番地に飛びましたね。で、何故か alltraps 手続きに制御が移っております。

(gdb) si
=> 0x5: push   $0x1c
0x00000005 in ?? ()
(gdb) si
=> 0xa: push   $0x0
0x0000000a in ?? ()
(gdb) si
=> 0xc: mov    $0x9,%eax
0x0000000c in ?? ()
(gdb) si
=> 0x11:        int    $0x40
0x00000011 in ?? ()
(gdb) si
=> 0x1053b4 <vector64+2>:       push   $0x40
320       pushl $64
(gdb) si
=> 0x1053b6 <vector64+4>:       jmp    0x104cd0 <alltraps>
321       jmp alltraps
(gdb) si
=> 0x104cd0 <alltraps>: push   %ds
9         pushl %ds
(gdb) si
=> 0x104cd1 <alltraps+1>:       push   %es
10        pushl %es
(gdb) si
=> 0x104cd2 <alltraps+2>:       push   %fs
11        pushl %fs
(gdb) si
=> 0x104cd4 <alltraps+4>:       push   %gs
12        pushl %gs
(gdb) 

64 は 0x40 ですな。なんとなく exec してるような気もしますが、、
今、alltraps から trap 呼び出したあたり。で、短気を起こして next すると swtch に制御が移りますな。
とりあえず 0x0 番地に jmp したのが見れたのでヨシ、ってことにしてもらっても良いかなぁ。しかもよく考えたら exec した先はプロセスなオブジェクトとして別なはずなので、トレイスは無理なのかどうか。

そのに

ズルしました。dmesg に出てくる

Memory: 4043492k/5242880k available

みたいなナニで arch/i386 配下を find|grep したら以下。

$ find|xargs grep 'Memory:'
./mm/init.c:    printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n",
$

上記出力な命令があるのは mem_init という手続きな模様。これ、main.c で呼び出されてるのかな。ただ、なんとなく mem_init で size してるのではなくて start_kernel 手続きの

	setup_arch(&command_line, &memory_start, &memory_end);

でセットされてるように見えます。ただし、arch/i386/mm/init.c の mem_init 手続きでは出力部分が以下の記述になってますので、

    printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n",
        (unsigned long) nr_free_pages << (PAGE_SHIFT-10),
        max_mapnr << (PAGE_SHIFT-10),
        codepages << (PAGE_SHIFT-10),
        reservedpages << (PAGE_SHIFT-10),
        datapages << (PAGE_SHIFT-10),
        initpages << (PAGE_SHIFT-10));

こん中でもごもごヤッてると言えばそうなのかも。
ざくっと確認したところでは setup_arch で (あるいはそれ以降でも?) 始点と終点をキメておいて mem_init の中で予約済みなページだったりを分別してそうなカンジです。
ここはもう少しじっくり見てみたい。