xv6のbootloader

f:id:varmil:20171106122940p:plain

xv6_translate/chapter1.md at master · msyksphinz/xv6_translate · GitHub

original UNIX v6

  1. ROMに保存されているブートストラップローダプログラムが、ルートディスクのブロック番号0にあるブートストラッププログラムをメモリのアドレス0に読み込んで実行する。
  2. ブートストラッププログラムはルートディスクのファイルシステムから /unix や /rkunixといったカーネルプログラム本体をメモリのアドレス0に読み込んで実行する。
  3. カーネルがシステムの初期化を行う

電源ON〜kernelをディスクにロード、PCを設定するところまで (1, 2)

わかりやすい

msyksphinz.hatenablog.com

kernel動作開始からinit前まで(3: 主に仮想メモリの初期化)

kernel.ld (リンカスクリプト)

ENTRY(_start)

ENTRY(start)とは、プログラムの実行をstartというシンボルの位置から開始するという意味です。startは.text : { start = . ; ...によって、機械語命令を格納する.textセクションの先頭に設定されます。

_startの位置からしばらく続くBYTE()やLONG()の列によって、所定の機械語命令を直接.textセクションに書き込んでいます。この命令をC風に書くと、だいたい次のようになります。

GNU linker scriptでhello world - Qiita

entry.S

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

MITのxv6を読もう - 第1章 entryはどのような構造? - - FPGA開発日記

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,
};

entry用のページテーブルはmain.cに定義されている。 (main.cのその部分だけ以下に載せておきます。ただmain.c全体でも115行しかありません。)