さて、次はbootasm.S とbootmain.cの勉強だ。
※ この記事はまだ勉強中のため、いろいろ間違いがあるかもしれません。
http://pdos.csail.mit.edu/6.828/2014/xv6/book-rev8.pdf
http://pdos.csail.mit.edu/6.828/2014/xv6/xv6-rev8.pdf
# Physical address line A20 is tied to zero so that the first PCs # with 2 MB would run software that assumed 1 MB. Undo that. seta20.1: inb $0x64,%al # Wait for not busy testb $0x2,%al jnz seta20.1 movb $0xd1,%al # 0xd1 -> port 0x64 outb %al,$0x64 seta20.2: inb $0x64,%al # Wait for not busy testb $0x2,%al jnz seta20.2 movb $0xdf,%al # 0xdf -> port 0x60 outb %al,$0x60
このあたり、何をしているんだろうと思ったら、キーボードの設定か。詳細はこちらを検索して見つけた。 inbで呼ばれるアドレスの中で0x00から0xffまではマザーボードの仕様に委ねられている。さらに検索すると、0x64はキーボードのステータスを示しているのか。
Keyboard scancodes: The AT keyboard controller
まずは、キーボードコントローラレジスタの2ビット目をチェックして、何らかのキーが入力されていないかをチェックする。 次に、0xd1を出力する。これはどういう意味だろう。パリティエラーとかが出力されるけど大丈夫なんかいな。
次は0x60に0xdfを出力している。これもキーボードコントローラバイトだ。
.code32 # Tell assembler to generate 32-bit code now. start32: # Set up the protected-mode data segment registers movw $(SEG_KDATA<<3), %ax # Our data segment selector movw %ax, %ds # -> DS: Data Segment movw %ax, %es # -> ES: Extra Segment movw %ax, %ss # -> SS: Stack Segment movw $0, %ax # Zero segments not ready for use movw %ax, %fs # -> FS movw %ax, %gs # -> GS
これは、32ビットモードに設定するコードかな。詳細は勉強していないけど、コメントからそういう事だと思う。
次は、bootmainを呼び出している。いよいよ、bootmain.cに移る。
void bootmain(void) { struct elfhdr *elf; struct proghdr *ph, *eph; void (*entry)(void); uchar* pa; elf = (struct elfhdr*)0x10000; // scratch space // Read 1st page off disk readseg((uchar*)elf, 4096, 0); // Is this an ELF executable? if(elf->magic != ELF_MAGIC) return; // let bootasm.S handle error // Load each program segment (ignores ph flags). ph = (struct proghdr*)((uchar*)elf + elf->phoff); eph = ph + elf->phnum; for(; ph < eph; ph++){ pa = (uchar*)ph->paddr; readseg(pa, ph->filesz, ph->off); if(ph->memsz > ph->filesz) stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz); } // Call the entry point from the ELF header. // Does not return! entry = (void(*)(void))(elf->entry); entry(); }
セクタのロードか。readsegは、回り回って、最終的にreadsectを読んでいる。
// Read a single sector at offset into dst. void readsect(void *dst, uint offset) { // Issue command. waitdisk(); outb(0x1F2, 1); // count = 1 outb(0x1F3, offset); outb(0x1F4, offset >> 8); outb(0x1F5, offset >> 16); outb(0x1F6, (offset >> 24) | 0xE0); outb(0x1F7, 0x20); // cmd 0x20 - read sectors // Read data. waitdisk(); insl(0x1F0, dst, SECTSIZE/4); }
これでデータを読み込んでいる。途中については省略するが、プログラムセグメントを全て読みこんだら、elfのエントリポイントを調査して、ジャンプする、という仕組みらしい。
// Call the entry point from the ELF header. // Does not return! entry = (void(*)(void))(elf->entry); entry();
ここから先も、さらに調査を進める。