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 しております。