書いたもの
-
xv6を学ぶ上での準備
-
起動処理を読む(main.c の main() が呼ばれるまで)
-
main() からmpmain()が呼ばれるまで
xv6を学ぶ上での準備
起動処理を読む(main.c の main() が呼ばれるまで)
main() からmpmain()が呼ばれるまで
.global main # main シンボルを公開する
.data
msg: .ascii "Hello, World\n"
asm(
"movl $0x01, $eax;"
"addl $0x05, $eax"
);
int result;
asm(
"movl $0x01, %%eax;"
"addl $0x05, %%eax"
: "=a" (result)
);
上記のコードはresultに addl した結果を格納するもの
前半のアセンブリっぽい記法のところを「アセンブリテンプレート」と呼ぶ
: 以降が出力オペランドの指定
また、拡張アセンブリには以下のように入力値にC言語の変数を使うことができる
int x = 1, y = 5;
int result;
asm(
"movl %2, %%eax;"
"addl %1, %%eax"
: "=a" (result)
: "m" (x), "m" (y)
);
A20 ゲート
リアルモード
プロテクトモード
ディスクリプタ
GDT
LDT
IDT
# Start the first CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.
.code16 # Assemble for 16-bit mode
.globl start
start:
cli # BIOS enabled interrupts; disable
以下コメント文直訳
まずは 16 bit mode であることを明示しつつ、startが呼ばれる
cli という命令は x86 CPU の命令で IF(Interrput enable Flag)というシステムフラグがクリアされて、外部からのハードウェア割り込みに応答しなくなる。
というわけで、ハードウェア割り込みを禁止している
次にレジスタの初期化
# Zero data segment registers DS, ES, and SS.
xorw %ax,%ax # Set %ax to zero
movw %ax,%ds # -> Data Segment
movw %ax,%es # -> Extra Segment
movw %ax,%ss # -> Stack Segment
明らかだが、xorを取って ax レジスタのクリアをした後に as レジスタの値を使って ds, es, ss の各セグメントをクリアしている
A20ゲートの解放
# 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
To this day, modern PCs continue this backwards compatibility dance, and low-level software probably continues to depend on 8088 behavior at boot. The boot sector must enable the A20 line using I/O to the keyboard controller on ports 0x64 and 0x60.
とあるのだがなんで、0x64、0x60なのかがまだ分からない。
XXX: あとで詳しく調べる
とはいえ、これで1MiB以上のメモリを使えるようになる、ということだ
32bit protected mode への移行(GDT の設定、32bit protected mode になったときの jmp について)
# Switch from real to protected mode. Use a bootstrap GDT that makes
# virtual addresses map directly to physical addresses so that the
# effective memory map doesn't change during the transition.
lgdt gdtdesc
movl %cr0, %eax
orl $CR0_PE, %eax
movl %eax, %cr0
//PAGEBREAK!
# Complete transition to 32-bit protected mode by using long jmp
# to reload %cs and %eip. The segment descriptors are set up with no
# translation, so that the mapping is still the identity mapping.
ljmp $(SEG_KCODE<<3), $start32
.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
# Set up the stack pointer and call into C.
movl $start, %esp
call bootmain
イヤー長い。。。じっくり後で読む
bootmain.c への jump 実行後
あくまで俯瞰
詳しい説明は違うページに分けて読むことにする
main() が呼ばれた後に何が実行されるかソースコードをじっくり見ていく
int
main(void)
{
kinit1(end, P2V(4*1024*1024)); // phys page allocator
kvmalloc(); // kernel page table
mpinit(); // collect info about this machine
lapicinit();
seginit(); // set up segments
cprintf("\ncpu%d: starting xv6\n\n", cpu->id);
picinit(); // interrupt controller
ioapicinit(); // another interrupt controller
consoleinit(); // I/O devices & their interrupts
uartinit(); // serial port
pinit(); // process table
tvinit(); // trap vectors
binit(); // buffer cache
fileinit(); // file table
iinit(); // inode cache
ideinit(); // disk
if(!ismp)
timerinit(); // uniprocessor timer
startothers(); // start other processors
kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // must come after startothers()
userinit(); // first user process
// Finish setting up this processor in mpmain.
mpmain();
}
それぞれの初期化について淡々と見ていく
kinit1() (kalloc.c)
kvmalloc() (vm.c)
mpinit() (mp.c)
lapicinit() (lapic.c)
seginit() (vm.c)
picinit() (main.c)
ioapicinit() (ioapic.c)
consoleinit() (console.c)
uartinit() (uart.c)
pinit() (proc.c)
tvinit() (trap.c)
binit() (bio.c)
fileinit() (file.c)
iinit() (fs.c)
ideinit (ide.c)
if (!ismp) timerinit() (timer.c)
startothers() (main.c)
kinit2( .... ) (kalloc.c)
userinit() (proc.c)
mpmain() (main.c)