xv6

新年読み初め。
こないだ clone したソースは rev6 になるのかどうか。とりあえずイベント待ち (?) になるまでの初期処理な部分を確認しておきたいという方向で。
とりあえず Makefile から確認開始。例えばターゲットが qemu-gdb とするとルールな記述が以下。

qemu-gdb: fs.img xv6.img .gdbinit
	@echo "*** Now run 'gdb'." 1>&2
	$(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB)

QEMUOPTS が以下なカンジ。

QEMUOPTS = -hdb fs.img xv6.img -smp $(CPUS) -m 512 $(QEMUEXTRA)

あるいは fs.img なルール記述が以下なカンジ。

mkfs: mkfs.c fs.h
	gcc -m32 -Werror -Wall -o mkfs mkfs.c

UPROGS=\
	_cat\
	_echo\
	_forktest\
	_grep\
	_init\
	_kill\
	_ln\
	_ls\
	_mkdir\
	_rm\
	_sh\
	_stressfs\
	_usertests\
	_wc\
	_zombie\

fs.img: mkfs README $(UPROGS)
	./mkfs fs.img README $(UPROGS)

fs.img および mkfs 云々はとりあえずスルーします。xv6.img なルールが以下。

xv6.img: bootblock kernel fs.img
	dd if=/dev/zero of=xv6.img count=10000
	dd if=bootblock of=xv6.img conv=notrunc
	dd if=kernel of=xv6.img seek=1 conv=notrunc

おそらく bootblock が先頭セクタにハマる 512bytes な形になってて、以降で kernel が出力されてる、ということなのかどうか。
bootblock のルール記述が以下。

bootblock: bootasm.S bootmain.c
	$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
	$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
	$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
	$(OBJDUMP) -S bootblock.o > bootblock.asm
	$(OBJCOPY) -S -O binary -j .text bootblock.o bootblock
	./sign.pl bootblock

sign.pl で末端 "\x55\xAA" 出力している模様。perl の呪文に興味しんしんですが、詳細はスルーで。あと、0x7C00 はこの先頭セクタが load されるナニに沿っている模様、なのかな。
エントリポイントは start とのことなので bootasm.S からになるのか。基本的には

  • 割り込み disable
  • %ds、%es、%ss 初期化
  • A20 初期化
  • 仮のプロテクトモードに移行
  • bootmain 呼び出し

で、bootmain.c の bootmain 手続きが呼び出されるんですが bootmain 手続きでは

  elf = (struct elfhdr*)0x10000;  // scratch space

な領域で elf フォーマットを云々している模様。readseg という手続きで 1 番目なセクタからデータを取得している模様です。で、ELF なナニを元に手続きを呼び出しております。
ちょっと Makefile に手を戻すと kernel のルール記述は以下になってます。

kernel: $(OBJS) entry.o entryother initcode kernel.ld
	$(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o $(OBJS) -b binary initcode entryother
	$(OBJDUMP) -S kernel > kernel.asm
	$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym

ええと、kernel.ld を確認してみたらエントリポイントは entry というシンボルらしい。entry なシンボルは entry.S にて定義されております。
ちょっと掘削がざっくりに過ぎる気がしますがスルーして以下な手続きな模様。

# Entering xv6 on boot processor, with paging off.
.globl entry
entry:
  # Turn on page size extension for 4Mbyte pages
  movl    %cr4, %eax
  orl     $(CR4_PSE), %eax
  movl    %eax, %cr4
  # Set page directory
  movl    $(V2P_WO(entrypgdir)), %eax
  movl    %eax, %cr3
  # Turn on paging.
  movl    %cr0, %eax
  orl     $(CR0_PG|CR0_WP), %eax
  movl    %eax, %cr0

  # Set up the stack pointer.
  movl $(stack + KSTACKSIZE), %esp

  # Jump to main(), and switch to executing at
  # high addresses. The indirect call is needed because
  # the assembler produces a PC-relative instruction
  # for a direct jump.
  mov $main, %eax
  jmp *%eax

あら、ここで page directory セットしてるのかな。最初の仮置きのヤツなのかな。定義は main.c にて以下。

// Boot page table used in entry.S and entryother.S.
// Page directories (and page tables), must start on a page boundary,
// hence the "__aligned__" attribute.  
// Use PTE_PS in page directory entry to enable 4Mbyte pages.
__attribute__((__aligned__(PGSIZE)))
pde_t entrypgdir[NPDENTRIES] = {
  // Map VA's [0, 4MB) to PA's [0, 4MB)
  [0] = (0) | PTE_P | PTE_W | PTE_PS,
  // Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB)
  [KERNBASE>>PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS,
};

最初の一つのみ、になってますね。まだ kernel しか動いてないから大丈夫、ということなのか。
で、stack を云々して main な手続きに jmp しております。