MIT JOS 理解lab1

最近开始做MIT的JOS,做了一段时间了,打算总结一下,作为之后几个实验的基础,错误估计会不少,还请大家多多指教。

一.Lab1-Booting a PC

Lab1是关于PC的启动过程,PC启动遵循BIOS加载(实模式)->BIOS跳转到Boot loader(JOS中的地址是0x7c00)->Boot loader导入OS内核的过程。第一步中大家可以使用gdb查看PC上电后从0xfe05b开始执行的每一条BIOS汇编指令,基本上是关于一些硬件IO的设置,这里就不再多说了,对这些BIOS指令感兴趣的同学可以参看这里

接下来就是跳转到boot loader,首先执行boot.s中的汇编代码,这里进行实模式进入保护模式的转换。首先设定了几个常数的值,如PROT_MODE_CSEG=0x8,这是代表在新的段中代码段的段偏移为0x8;接着我们看关于gdt的定义(在boot.s的最后几行),这里定义了跳转到32位保护模式后使用的GDT,注意在之后加载内核后还会重新使用内核定义的GDT,两者是有区别的。这里定义了GDT的三个段:空段(SEG_NULL)、代码段(code seg)和数据段(data seg),以代码段的定义为例:SEG(STA_X|STA_R, 0x0, 0xffffffff)(大家可以参阅inc/mmu.h 第145行关于SEG的宏定义,SEG接受的三个参数分别是为了定义段的类型、基地址和段限),这句定义了代码段的类型、基地址和界限,可以看到基地址为0x0,即此时虚拟地址=物理地址,相当于关闭了段映射。接下来就可以看实模式跳转到32位保护模式的代码了:

1.Cli关中断,确保此时boot.s是唯一执行的程序;

      2.使能A20位,这一步有兴趣的自己google吧 O(∩_∩)O~与理解跳转关系不大

              3.lgdt gdtdesc //装入定义好的gdtgdtdesc是紧接gdt定义的gdt地址

movl %cr0, %eax //接下来这三句修改cr0寄存器的PE位,使能32位保护模式

orl $CR0_PE_ON, %eax

movl %eax, %cr0

             4.长跳转指令ljmp $PROT_MODE_CSEG, $protcseg跳转到下一条代码,目的是跳过剩余的16位指令。此处我们看到新     的GDT已经发挥作用,seg= PROT_MODE_CSEG, offset=protcseg,因为CSEG的基地址为0,则程序跳转到了代码段的protcseg偏移处

             5.那protcseg在哪儿?很显眼!它就在下面,里面的指令重新写入了DS、ES等段寄存器的值,最后一句跳转到bootmain函数,开始读入内核,OK,实模式跳转到保护模式完成!Bootmain函数位于boot/main.c中,是从硬盘扇区读取内核的操作,各种IDE IO指令很麻烦,有耐心的童鞋自己慢慢体会吧O(∩_∩)O~

           以上所述的这些指令都可以在编译好的obj/boot/boot.asm中看到,建议大家多看看这个文件,能对整个流程有个比较清晰的了解。

          跳转到JOS内核后,执行的第一个文件是kern/entry.s。这个文件的主要作用和布局同上述的boot.s差不多,都是建立新的GDT的过程,这里为什么需要新的GDT?原因就是内核的link address和load address不同,为了寻址到正确的物理地址,我们必须把虚拟地址减去KERNBASE,得到正确的物理地址,这也就是新的gdt(定义在entry.s最后几行,mygdt)中,codeseg和dataseg的基地址都定义成了-KERNBASE的原因了~

         但两个文件也有几个不同,一是entry.s中定义了一个RELOC(x)宏,RELOC(x)=((x) - KERNBASE),这是为了用于lgdt指令中加载新的gdt地址,因为此时新的gdt还没有生效,仍然执行的是旧的gdt,即段基址=0;二是定义了函数调用栈基址movl $0x0,%ebp,这一句也是Exercise 10循环跳转的结束条件。随后内核调用了i386_init()函数,这个就是之后几个实验的关键啦~

         至于几个Exercise,个人感觉难度不大,毕竟网上也有相应的源代码,汗…我做的清华的这个版本有个额外的练习——Detecting Processor Features,是读取Eflags寄存器得到CPU的有关信息,读写寄存器的函数也都实现好了,调用检测一下就好。为了翻转该寄存器值的第21位,采用异或1的方式,相应的主要代码贴在下面:

bool

is_cpuid_supported()

{

uint32_t eflags=read_eflags();

write_eflags(eflags^0x00200000);

if(read_eflags()==eflags) return 0;

return 1;

}

by feitian

posted @ 2010-11-29 17:13  MSRA_SE_TEAM  阅读(1899)  评论(0编辑  收藏  举报