そもそも、僕はx86のページテーブルの構造が分かってねえや(笑)。
まずはいろいろ調べてみよう。
Intelのx86では、セグメント方式とページング方式を同時に利用することができるのか。xv6ではページングの方式を利用している。
- セグメント: セグメントのサイズ、開始位置を自由に選択可能 → より複雑なハードウェアが必要になる?
- ページング: ページのサイズは固定、開始位置はあるオフセットに固定 → ハードウェアが簡単になる?
難しいから、commentaryにやってみよう。以下が、xv6が最初に実行するentryだ。
1032 # By convention, the _start symbol specifies the ELF entry point. 1033 # Since we haven’t set up virtual memory yet, our entry point is 1034 # the physical address of ’entry’. 1035 .globl _start // コンパイル時には仮想アドレスとして定義されるが、entryはページングが動作していない状態でスタートするので、物理アドレスに変換している? // ここはちょっとトリッキーだ。entryの場所を仮想アドレスの場所(0x8000_0000以上)に設定するのではなく、無理矢理0x0000_0000の付近に設定している。 // これにより、最初はページングハードウェアがONになっていなくても、entryを読み込んで実行が開始される。 1036 _start = V2P_WO(entry) 1037 1038 # Entering xv6 on boot processor, with paging off. 1039 .globl entry 1040 entry: // ページング機構をONにするプログラムだと思う。それ以上は調べていない。 1041 # Turn on page size extension for 4Mbyte pages 1042 movl %cr4, %eax 1043 orl $(CR4_PSE), %eax 1044 movl %eax, %cr4 ============================================================================ // entrypgdirの詳細は以下 1311 pde_t entrypgdir[NPDENTRIES] = { 1312 // Map VA’s [0, 4MB) to PA’s [0, 4MB) 1313 [0] = (0) | PTE_P | PTE_W | PTE_PS, // 0x0-0x400000 --> 0x00000に変換 1314 // Map VA’s [KERNBASE, KERNBASE+4MB) to PA’s [0, 4MB) 1315 [KERNBASE>>PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS, // 0x8000_0000-0x8040_0000 を 0x0000000に変換 1316 }; ============================================================================ 1045 # Set page directory 1046 movl $(V2P_WO(entrypgdir)), %eax // cr3に設定することにより、ページング変換はこの場所から変換を開始するようになる。 1047 movl %eax, %cr3 // ページングを有効にする。それ以上は調べていない。 1048 # Turn on paging. 1049 movl %cr0, %eax 1050 orl $(CR0_PG|CR0_WP), %eax 1051 movl %eax, %cr0 1052 // スタックを有効にする 1053 # Set up the stack pointer. 1054 movl $(stack + KSTACKSIZE), %esp 1055 1056 # Jump to main(), and switch to executing at 1057 # high addresses. The indirect call is needed because 1058 # the assembler produces a PC−relative instruction 1059 # for a direct jump. // mainは0x8000_0000以上の場所になる。従って、ここからはページングハードウェアを利用して仮想アドレス変換が始まる。 // ここから先は、ページングハードウェアによって、変換されるが、mainは0x8010_0000に存在しているつもりが、0x0000_0000に変換される。 1060 mov $main, %eax 1061 jmp *%eax 1062 1063 .comm stack, KSTACKSIZE