- run_loadfile関数から引き渡された引数をセーブ。
- EFLAGS registerを初期化。
- CPUの種類の特定とCPU性能情報の取得。
- 現在のstackは、boot lorderプログラムで使用していたstackなので、kernelエリアのstack(と言っても一時的な)に変更。
- bss[非初期化変数]領域のzeroクリア。
- ページングの設定。
- init386関数をcallして、i386に依存する初期設定を行う。
Kernelが本格的に動き出すためには、ページングの機能を有効にして、仮想記憶などの設定を行う必要があります。ページングが有効になるとkernelはリニアアドレスのd01000000H
番地にマッピングされ、kernelはこのアドレス上で動作するようになります。(もちろん、自動的にd01000000H
番地にマッピングされる訳でなく、そのように設定を行う)。Kernelは論理アドレスで動作する事を前提としているので、Kernel内で使用されるStatic変数などのアドレスは論理アドレス上の番地に配置されています。
しかし、この時点ではまだページングが無効であり、セグメント機構しか働いていないので、kernelは物理アドレス上で動作しています。この場合、ページングの設定を行うまで、あるラベルにデータをストアする時は、そのラベルのアドレスを 論理アドレス -> 物理アドレス変換してからストアする必要があります。
objdump -d /bsd | less bsd: file format elf32-i386 Disassembly of section .text: d0100120 <start>:<- 論理メモリ上のマッピングはd0100120H番地だが実際には物理メモリ上の100120H番地に存在する。 d0100120: 66 c7 05 72 04 00 00 movw $0x1234,0x472 d0100127: 34 12 d0100129: 8b 44 24 04 mov 0x4(%esp,1),%eax d010012d: a3 bc 95 52 00 mov %eax,0x5295bc d0100132: 8b 44 24 08 mov 0x8(%esp,1),%eax d0100136: a3 20 48 53 00 mov %eax,0x534820 d010013b: 8b 44 24 10 mov 0x10(%esp,1),%eax d010013f: 85 c0 test %eax,%eax
上記の問題を吸収するためlocoreのソース中には、RELOCマクロが存在します。RELOCマクロの中身は次のように定義されています。
#define RELOC(x) ((x) - KERNBASE)
このマクロは現在のアドレスからKERNBASEを減算しています。KERNBASEはページング有効時に、kernelがマップされる領域の先頭アドレスが定義されていて、これを減算する事により「論理アドレス→物理アドレス」の変換を行ないます。
start: movw $0x1234,0x472 # warm boot movl 4(%esp),%eax movl %eax,RELOC(_C_LABEL(boothowto)) は、次のように変換される。 d0100020 start: d0100020: 66 c7 05 72 04 00 00 movw $0x1234,0x472 d0100027: 34 12 d0100029: 8b 44 24 04 mov 0x4(%esp,1),%eax d010002d: a3 38 80 3a 00 mov %eax,0x5295bc <- 物理アドレスに変換されている 実際のboothowtoは次のアドレス d05295bc g O .data 00000004 boothowto
BIOSに対して"warm boot"の設定を行います。これにより、リブートした時にシステム診断が省略され、マシンの立上げが多少早くなります。
BISOのアドレス0x472
に値を書込む事により、ブート時の動作を変更出来る。
0000h
(cold boot):メモリチェックを行う。メモリの内容は書き変わる。1234h
(warm boot):メモリチェックを行わない。メモリの内容を保証しない。4321h
(warm boot):メモリチェックを行わない。メモリの内容を保持する。boot関数から渡された引数を変数に保存する。
EFLAGS registerをPSL_MBOで初期化。FS・GSレジスタをゼロクリア。
80486以降のCPUではcpuid命令により、CPUの詳しい情報が取得できるようになっていますが、80386 CPUや80486互換であってもcpuid命令をサポートしていないCPUに対しては、フラグの動作でCPUの種別を判別します。
cpuid命令が使えないCPUの判定は、EFLAGSレジスタ上のフラグの有無と演算結果でのフラグの動作状態で行います。具体的には次のようなアルゴリズムになります。
if(EFLAGS の ac[Alignment Check]が無効){ if( 符合無し除算 (0x5555/2)の結果 ZF[Zero Flag]が変化しない){ NexGen 586 CPU } else{ Intel 386 CPU } } else if(EFLAGS の ID[ID flag]が無効){ if( 符合無し除算 (0x5555/2)の結果 CF[Carry Flag]が変化する){ Intel 486 CPU } else if(符合無し除算 (0xffffffff/4)の結果 C,PF,AF,Z,N,Vが変化する ){ Cyrix 6x86 CPU } else{ Cyrix 486DLC CPU CACHE動作の設定(config時のパラメータにより、無効/有効を選択) } } else{ cpuidを実行 }
cpuid命令を使った検出は次のようになります。
Vendor IDを取得 cpuid(ax=0) CPUの種類と性能で取得 cpuid(ax=1) if(CPUにCache有り){ Cache情報を取得 cpuid(ax=2) }
上記で取得した情報は、下記の変数に格納されます。
変数 | 内容 |
---|---|
cpu | arch/i386/include/cputypes.hに定義された値が入る |
cpuid_level | CPUIDの入力(AX Register)出来るMax値 |
cpu_vendor | Vendor ID |
cpu_id | Type,Model,Family... |
cpu_feature | CPUの拡張機能 |
cpu_cache_eax | Cache,TLB |
cpu_cache_ebx | Cache,TLB |
cpu_cache_ecx | Cache,TLB |
cpu_cache_edx | Cache,TLB |
boot時に使用していたのスタックから、ページングを設定するまでの一時的なスタックtmpstk(512byteの大きさ)に変更する。
長くなりそうなので、locoreでのページング設定とページを分けました。