在操作系统初始化的过程中,完成硬件初始化等工作之后,CPU即切换到保护模式。从实模式切换到保护模式相对简单,首先定义GDT表,针对代码段、数据段等都要定义对应的表项,并把GDT表的起始物理地址加载到GDTR寄存器,接下来把CR0寄存器的第一个比特设置为1,最后采用一条段间跳转指令,即可切入保护模式。在Hello China的最初版本中,切换到保护模式之后,就彻底告别了实模式。但是在Hello China V1.75的实现中,为了读写磁盘、设置显示器工作模式等工作,还需要从保护模式切换回实模式,通过BIOS调用完成这些工作。下面大致介绍一下如何从保护模式切换回实模式,供读者参考。
按照Intel IA32编程指导手册上的描述,从保护模式切换到实模式,需要遵循下列步骤:
(1)关闭中断,即执行CLI指令清除CPU Eflags寄存器的中断允许位。
(2)如果启用了分页功能(Hello China缺省启用了分页功能),则需执行下列步骤。
1)跳转到一段线性地址和物理地址相同的代码,即经过分页机制映射后的线性地址与实际物理地址相同的代码段。由于Hello China系统空间的映射地址与物理地址相同,因此该步骤可省略。分页机制及相关概念,可参考第5章获取更多信息。
2)确保IDT和GDT在物理地址和虚拟地址相同的页面中,这很容易做到。
3)清除CR0寄存器的PG比特(第32比特),即关闭分页机制。上述两个步骤的要求,就是为关闭分页机制奠定基础。
4)对CR3寄存器清零,以刷新TLB。
(3)跳转到一个长度是64KB的代码段,这可确保CS寄存器保持与实模式相同的段长度限制。这样就必须在GDT中增加一个段描述符,这个段描述符的段长度是64KB。
(4)向DS/SS/FS/ES/GS等段寄存器装载一个满足下列条件的选择符,以确保切换到实模式时,这些寄存器能够与实模式要求吻合。
1)段长度限制是64KB。
2)以BYTE为粒度,而不是以缺省的4KB为长度粒度。
3)向上扩展。(www.xing528.com)
4)可写入。
5)基地址可以是任何值。
(5)执行LIDT指令,确保中断寄存器指向一个实模式中断表。在PC启动时,中断描述表位于内存的开始处,长度为1024B。由于在Hello China启动过程中这部分内存没有被破坏,因此在这里可继续使用。
(6)清除CR0的PE比特(第0比特),正式进入实模式。
(7)执行一个远跳转指令,跳入实模式中的一段代码继续执行。这个跳转指令刷新指令预取队列,并加载CS寄存器。这时就正式进入了实模式,寻址方式按照“段基址向左移动4位,加上段偏移”的方式进行。
(8)按照实模式要求,装载DS/ES/FS/GS/SS等寄存器。如果有寄存器不使用,可装入0。
(9)执行STI指令,启用中断功能
最后,要确保上述代码位于同一个内存页面中,而且页面的虚拟地址与物理地址相同。在Intel 32位的CPU中,一个页面的缺省大小是4KB,这对上述代码来说是足够的。
在Hello China的实现中,从保护模式向实模式切换的代码,都是在realinit.asm文件中实现的。在编译的时候,这段模式切换代码被放在了realinit.bin模块内的2KB偏移处。再回忆一下Hello China的加载过程,realinit.bin模块被加载到物理内存4KB开始处的位置,共占用4KB的空间。这样模式切换部分代码即位于内存的6KB偏移处。在进行模式切换的时候,只需要跳转到这个位置,即可执行模式切换代码。实模式代码执行完毕,会重新返回保护模式。可使用通用寄存器在保护模式和实模式之间传递参数和返回值,下面是各个寄存器的分配情况:
(1)EAX寄存器给出了需要调用的BIOS服务代码,这个代码是Hello China自己定义的。实模式代码根据这个号码,决定调用哪种BIOS服务。调用返回的时候,EAX返回实模式执行结果是否成功的标志,0表示执行失败,否则表示成功。
(2)ECX、EDX、ESI、EDI、EBP分别存储了调用BIOS服务的参数,最多五个参数。后续如需要,可通过共享内存方式解决参数不足问题。
需要说明的是,从保护模式切换回实模式,比从实模式切换到保护模式要复杂得多。但是建议读者不要对这个过程太过关注,因为这个过程与操作系统的核心机制关系不大,对操作系统关键原理的理解也没有太多帮助,还是建议读者把更多的精力放在理解操作系统核心的实现机制上。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。