6.828: Operating System Engineering (12)
これ、読み込んだソレを x/i で確認せよ、ということなのかな。とりあえず
- 0x7c00 に breakpoint 設定
- continue
- 次に止める場所の確認 <- イマココ
という状況なんですが、boot.asm 見たら昨晩エントリな 0x7d7a に breakpoint 設定して 0x10018 以降のナニを確認してみれば良さげなカンジ。
違うのかな
こーゆーコトなの?
(gdb) x/i 0x7c00 0x7c00: cli (gdb) x/i 0x7c01 0x7c01: cld (gdb) x/i 0x7c02 0x7c02: xor %eax,%eax (gdb) x/i 0x7c04 0x7c04: mov %eax,%ds (gdb) x/i 0x7c06 0x7c06: mov %eax,%es (gdb) x/i 0x7c08 0x7c08: mov %eax,%ss
しかしよく考えてみるに、命令って 4 byte 境界で align なのかと思ったらそうではないんですね。てかそれって arm の話?
ちなみに今は 0x7d7a で止まってるのでちょっと進めてみます。
(gdb) si => 0x7d7f: and $0xffffff,%eax 0x00007d7f in ?? () (gdb) si => 0x7d84: call *%eax 0x00007d84 in ?? () (gdb) si => 0x10000c: movw $0x1234,0x472 0x0010000c in ?? () (gdb) x/i 0x10000c => 0x10000c: movw $0x1234,0x472
なんで 0x1000c なのかなぁ。0x7d7a あたりの処理が以下なんですが
7d7a: a1 18 00 01 00 mov 0x10018,%eax 7d7f: 25 ff ff ff 00 and $0xffffff,%eax 7d84: ff d0 call *%eax
jmp ではなくて call だから、ということなのかどうか。ちなみに少しだけ si で進めてみたんですが、
(gdb) si => 0x100015: lgdtl 0x110018 0x00100015 in ?? () (gdb) si => 0x10001c: mov $0x10,%eax 0x0010001c in ?? ()
kernel.asm によれば以下な命令、ということなのか。
_start: movw $0x1234,0x472 # warm boot lgdt RELOC(mygdtdesc) # load descriptor table f0100015: 0f 01 15 18 00 11 00 lgdtl 0x110018 movl $DATA_SEL, %eax # Data segment selector f010001c: b8 10 00 00 00 mov $0x10,%eax
なんというか色々な意味でマクロだぞ。
とりあえず
以下のソレに答えておいて次に進む。
- At what point does the processor start executing 32-bit code? What exactly causes the switch from 16- to 32-bit mode?
- What is the last instruction of the boot loader executed, and what is the first instruction of the kernel it just loaded?
- Where is the first instruction of the kernel?
- How does the boot loader decide how many sectors it must read in order to fetch the entire kernel from disk? Where does it find this information?
ええと、32-bit mode になるのは以下の命令ですよね。
# Jump to next instruction, but in 32-bit code segment. # Switches processor into 32-bit mode. ljmp $PROT_MODE_CSEG, $protcseg 7c2d: ea 32 7c 08 00 66 b8 ljmp $0xb866,$0x87c32
前準備として
- cli して cld
- ds、es、ss を 0 で初期化
- A20 マスク解除 (?)
- GDT セット
- cr0 の PE フラグ設定
で ljmp してプロテクトモードに、という形なのかな。詳細は微妙です。なんとなくそう読める、という程度の理解だったりしてます。理解が微妙なので控えておくと、boot.asm の記述としては以下で
00007c0a <seta20.1>: # Enable A20: # For backwards compatibility with the earliest PCs, physical # address line 20 is tied low, so that addresses higher than # 1MB wrap around to zero by default. This code undoes this. seta20.1: inb $0x64,%al # Wait for not busy 7c0a: e4 64 in $0x64,%al testb $0x2,%al 7c0c: a8 02 test $0x2,%al jnz seta20.1 7c0e: 75 fa jne 7c0a <seta20.1> movb $0xd1,%al # 0xd1 -> port 0x64 7c10: b0 d1 mov $0xd1,%al outb %al,$0x64 7c12: e6 64 out %al,$0x64 00007c14 <seta20.2>: seta20.2: inb $0x64,%al # Wait for not busy 7c14: e4 64 in $0x64,%al testb $0x2,%al 7c16: a8 02 test $0x2,%al jnz seta20.2 7c18: 75 fa jne 7c14 <seta20.2> movb $0xdf,%al # 0xdf -> port 0x60 7c1a: b0 df mov $0xdf,%al outb %al,$0x60 7c1c: e6 60 out %al,$0x60
これ、手元の kernel な source によれば以下かなと。
static void enable_a20_kbc(void) { empty_8042(); outb(0xd1, 0x64); /* Command write */ empty_8042(); outb(0xdf, 0x60); /* A20 on */ empty_8042(); outb(0xff, 0x64); /* Null command, but UHCI wants it */ empty_8042(); }
あと仮で作成されていると思われる gdt は boot.S によれば以下。
# Bootstrap GDT .p2align 2 # force 4 byte alignment gdt: SEG_NULL # null seg SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg SEG(STA_W, 0x0, 0xffffffff) # data seg gdtdesc: .word 0x17 # sizeof(gdt) - 1 .long gdt # address gdt
仮、なのかなぁ。しかもなんとなくフォーマットが微妙な気がするんですがマクロの定義を見たら大丈夫そげでした。
次次
ええと、ELF が云々とあるがスルーしても良いらしい。とりあえず Excersize 5 を確認してみましょうね。
make qemu-gdb して gdb 起動して boot loader のエントリポイントに breakpoint を設定して確認。
(gdb) b *0x7c00 Breakpoint 1 at 0x7c00 (gdb) c Continuing. [ 0:7c00] => 0x7c00: cli Breakpoint 1, 0x00007c00 in ?? () (gdb) x/8x 0x00100000 0x100000: 0x00000000 0x00000000 0x00000000 0x00000000 0x100010: 0x00000000 0x00000000 0x00000000 0x00000000 (gdb)
で、Kernel が起動する所に breakpoint を設定して試験。
(gdb) b *0x7d84 Breakpoint 2 at 0x7d84 (gdb) c Continuing. The target architecture is assumed to be i386 => 0x7d84: call *%eax Breakpoint 2, 0x00007d84 in ?? () (gdb) si => 0x10000c: movw $0x1234,0x472 0x0010000c in ?? () (gdb) x/8x 0x00100000 0x100000: 0x1badb002 0x00000003 0xe4524ffb 0x7205c766 0x100010: 0x34000004 0x15010f12 0x00110018 0x000010b8 (gdb)
ええとこれ、何でしょ。obj/kern/kernel を objdump せよとか書いてるのかな。
$ objdump -f obj/kern/kernel obj/kern/kernel: file format elf32-i386 architecture: i386, flags 0x00000112: EXEC_P, HAS_SYMS, D_PAGED start address 0xf010000c $
なるほど。ロードされた、ってことが分かるって意味?
てか、次の項の Link vs. Load Address ってソレにも関係するのかどうか不明ですが、start address が 0xf01000c とあるけど上記の例にもあるように実際は
(gdb) si => 0x10000c: movw $0x1234,0x472 0x0010000c in ?? () (gdb)
なんスよね。次の Excersize が boot loader の
$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o $@.out $^
なアドレスを変えてリコンパイルして実行してみれ、とある。非常に面白そうなんですが時間が無い。
もしなんとかなるようであれば追記します。