现在的位置: 首页 > 综合 > 正文

MIT JOS 理解lab1

2012年11月12日 ⁄ 综合 ⁄ 共 2142字 ⁄ 字号 评论关闭

 

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

一.Lab1-Booting a PC

 

Lab1是关于PC的启动过程,PC启动遵循BIOS加载(实模式)->BIOS跳转到Boot
loaderJOS中的地址是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位保护模式的代码了:


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

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

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

 

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

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

抱歉!评论已关闭.