(一)关中断并将system移动到内存地址起始位置 0x00000
将CPU的标志寄存器(EFLAGS)中的中断允许标志(IF)置0。这样系统不会再响应中断,直到main函数中能够适应保护模式的中断服务体系重建完毕才会打开,那时候响应中断的服务程序将不再是BIOS提供的中断服务程序,而是系统自身提供的。
就是要完成实模式下的中断向量表和保护模式下的中断描述符表(IDT)的交接工作。借助关中断(cli)和开中断(sti)完成这个过程的创建,即在创建过程中不能去响应中断,否则没有对应的中断程序,系统会crash。
setup程序将位于 0x10000 的内核程序复制到内存地址起始位置 0x00000 处。本来0x00000这个位置存放的是由BIOS建立的中断向量表和BIOS数据区,这样它们都会被覆盖,所以在新的用于保护模式的中断服务体系建立完毕之前,操作系统不再具备响应并处理中断的能力。这就是关中断的意义。
为了建立32位操作系统,引入新的概念:中断描述附表(IDT)和全局描述符表(GDT)。
(二)打开A20,实现32位寻址
本来5个F,打开A20后,就变成了8个F,即4G。
对0.11来说,最大只能支持16MB的物理内存,但其线性地址空间已经是4GB。
实模式下CPU寻址范围为0~0xFFFFF,共1MB寻址空间,需要0~19号共20根地址线。进入保护模式后,将使用32位寻址模式,即采用32根地址线进行寻址,第21根(A20)至第32根地址线的选通控制将意味着寻址模式的切换。
(三)8259A保护模式下的重新编程
为了建立中断机制,就要知道8259A,它是可以用程序控制的中断控制器。单个8259A能管理8级向量优先级中断。
我们需要对8259A进行重新编程,因为CPU在保护模式下,int 0x00~int 0x1F 被Intel保留作为内部(不可屏蔽)中断和异常中断。如果不对8259A进行重新编程,int 0x00 ~ int 0x1F 中断将被覆盖。所以需要通过8259A编程将原来的IRQ0x00~IRQ0x0F对应的中断号重新分布,即在保护模式下,IRQ0x00~IRQ0x0F的中断号是 int 0x20~ int 0x2F。
setup程序通过下面的代码将CPU工作方式设为保护模式,将CR0寄存器第0位(PE)置1,即设定处理器工作方式为保护模式。
mov ax, #0x0001 ! protected mode(PE) bit
lmsw ax ! This is it!
lmsw: Load Machine Status Word
置处理器状态字。但是只有操作数的低4位被存入CR0,只有PE,MP,EM和TS被改写,CR0其他位不受影响。
CPU工作方式转变为保护模式后,一个重要的特征就是要根据GDT决定后续执行哪里的程序。有了GDT的引导,接下来就会跳转到head程序的开始位置。head.s开始执行。
实际上,我们所说的system程序,就是由head程序和main程序链接而成的,head在前,main紧挨着head。head程序做了个对内核程序在内存中的布局有很重大的意义的事情:创建分页机制,即在0x000000的位置创建了页目录表、页表、缓冲区、GDT、IDT。
再提一下,在实模式下,CS本身就是代码段基址,在保护模式下,CS本身不是代码段基址,而是代码段选择符。也就是说,从实模式转变为保护模式,段基址的使用方法和实模式差别非常大,要使用GDT产生段基址。
接下来要将页目录表和页表放在内存起始位置。它们被放置在从0x00000开始的内存位置。
之后就可以执行用32位编译器编译的main函数了。总之,head.s就是用来从开机时的16位实模式过渡到main函数执行需要的32位保护模式。