Living@Greatwall

entry.S

lab1提到bootloader的c文件最后会加载内核到物理地址0x10000处(64K)处,然后再执行内核的入口entry.S函数。

这里要特别注意:在此之前,所有的都是直接操作物理地址(怎么做到的?实时模式就不用说了,16位寻址1MB, 进入保护模式后,32位,bootloader把所有段寄存器设成0,除了esp,这个必须设置到正确的物理地址。 而且分页机制也没有使能,所以线性地址就是物理地址)。

在编译image时,kernel.ld会将内核的c代码的(entry.S仍在物理地址空间执行)起始地址链接到0xF0100000,也就是当内核执行c代码main时, 所有的指令的寻址都是以这个为基础,但是内核的实际物理地址是在0x100000,所以进入内核后第一件事就是要建立一个映射: 将起始地址为0xF0100000映射到物理地址0x100000(为以后的c代码执行准备好)。

内核入口函数entry.S中(仍在物理地址空间0x100000执行),一开始就是做这件事,使能分页机制(设置CR0):

 # Load the physical address of entry_pgdir into cr3.  entry_pgdir
 # is defined in entrypgdir.c.
 movl   $(RELOC(entry_pgdir)), %eax      #entry_pgdir是定义在内核的c代码中,所以其地址是以0xF0100000为基础,
                                         #所以RELOC(entry_pgdir)是把它转化为对应的物理地址
 movl   %eax, %cr3
 movl   %cr0, %eax # Turn on paging.
 orl    $(CR0_PE|CR0_PG|CR0_WP), %eax
 movl   %eax, %cr0
 # Now paging is enabled, but we're still running at a low EIP
 # (why is this okay?).  Jump up above KERNBASE before entering
 # C code.
 mov    $relocated, %eax
 jmp    *%eax
relocated:
 # Clear the frame pointer register (EBP)
 # so that once we get into debugging C code,
 # stack backtraces will be terminated properly.
 movl   $0x0,%ebp    # nuke frame pointer,这个用于lab1解析栈帧用作循环结尾的条件
 # Set the stack pointer
 movl   $(bootstacktop),%esp
 # now to C code
 call   i386_init
 # Should never get here, but in case we do, just spin.
spin:   jmp spin

此后,内核进入main的c代码,在虚拟地址空间执行。