Oranges操作系统-保护模式pmtest1、pmtest2、pmtest3代码分析
一、pmtest1.asm
下面对pmtest1.asm中的文件进行简单的分析 1.首先在先定义一个定义全局描述符表GDT的节[SECTION.gdt],GDT为操作系统提供了段式存储机制。 |
其中包括三个描述符,分别为 LABEL_GDT,LABEL_DESC_CODE32和LABEL_DESC_VIDEO,每一个描述符定义了一个段,第一个描述符段基址和段界限都为0;第二个描述符指向一个还没有规定大小的代码段,其段基址为0;第三个描述符指向的段基址为0B8000h,顾名思义正是显存。
下面这三行表明了GDT的长度,界限和基地址
下面来定义段选择子,段选择子是描述符索引,相当于数组的下标。这里分别定义了两个段选择子,其值也就等于所对应的段相对于GDT基地址的偏移量。
2.下面来定义[SECTION.s16]节
首先对各个寄存器和栈指针进行初始化
对之前定义的32位代码段描述符LABEL_DESC_CODE32进行初始化:首先将32位代码段的物理地址赋值给了eax寄存器,下面只需要把eax的值放入段描述符中即可定义32位代码段的段基址、段界限、段属性。由于在段描述符中的内存排列并不是按照段基址、段界限、段属性这样的顺序,所以将eax中保存的这个值分成三部分分别赋给LABEL_DESC_CODE32的相应偏移位置。也就是说下面程序的功能是把
32位程序段的物理首地址放到程序段描述符的段基址中,以便跳转到保护模式时,可以使用选择子选用程序段描述符,从而得到32位程序段的物理首地址。
然后将eax寄存器清零,而GDT的段地址储存在ds段寄存器中,偏移地址为LABEL_GDT,将GDT的物理地址GdtPtr数据结构之中,GdtPtr共有6个字节
将GdtPtr中指示的6字节加载到寄存器gdtr中,gdtr和GdtPtr的结构完全一致 由于保护模式下中断处理机制与实模式不同,接下来需要关闭中断 |
通过操作端口92h打开A20地址线
这里需要注意的是:A20地址线并不是打开保护模式的关键,只是在保护模式下,不打开A20地址线,你将无法访问到所有的内存
保护模式下,A20关闭(始终为0),则用户的地址只能是:0 - (1MB-1), 2 - (3MB-1), 4 - (5MB-1),我们可以这样设想,A20为个位数(以1MB为单位),如果它始终为0,你永远不可能让这个数变成奇数。保护模式下,A20开启,则可以访问全地址,没有奇偶MB的问题
将cr0寄存器的第0位PE位置为1,因为当此位为0时,CPU运行在实模式下,当此位置为1时,CPU运行在保护模式中 实际上,在执行完mov cr0,eax这行之后,CPU就已经运行在实模式下了,但是此时cs寄存器中的值仍为实模式下的值因此需要执行,这里使用的是段选择子+偏移量的寻址方式,也正是保护模式下的寻址方式。 跳转至描述符DESC_CODE32对应的段地址,也就是LABEL_DESC_CODE32的地址 3.[SECTION .s32]节 这节的主要内容是将一个红色的字符“P”保存在显存中,并计算了代码节的长度,将其保存在SegCode32Len中 二、pmtest2.asm
显示完毕以后,这段代码调用了四个函数,分别是DispReturn、TestRead、TestWrite,下面来逐一查看各函数的作用
TestWrite函数:与TestRead函数相对应,这个函数首先保存esi和edi寄存器的值,这是因为edi寄存器始终指向要显示的下一个字符的位置,以免在显示过程中产生混乱;接着不断的将si指向的储存单元读入al寄存器之中,当al的值不为空时,就把al中的字符填入es的相应偏移之中。也就是实现了向TEST段进行写数据的操作。需要补充的是:这里的OffsetStrTest既是字符串相对于LABEL_DATA 的偏移量,又是其在数据段中的偏移(因为数据段的基地址也正是LABEL_DATA的物理地址)
执行完上面的几个函数之后,代码将由 jmp SelectorCode16:0 这个语句跳转到一个新的段:[SECTION .s16code],这个段把SelectorNormal的值赋给了ds,es,fs,gs,gs,ss等寄存器,并将cr0寄存器的PE位清零,这是在为跳转回到实模式做准备。而结尾处的跳转指令看似段地址选择为了0,实际上在s16段的开始部分将会对段地址作出相应的修改,使程序能够正常的返回到实模式之中。
16位代码段的前半部分主要是对于各种段描述符的初始化过程,包括 16 位代码段描述符、32 位代码段描述符、数据段描述符、堆栈段描述符等等过程,与之前分析过的pmtest1.asm相差不大。而在这之后有一个LABEL_REAL_ENTRY,这是由保护模式跳转回实模式时所进入的地址,在这个地址下重新设置各个寄存器的值,恢复sp的值,然后关闭A20地址线,打开中断,将控制权重新交还给DOS。
三、pmtest3.asm
下面我们找到LDT对应的段,可以看到LDT描述符和选择子的定义与GDT几乎相同,而这里仅仅定义了一个LDT的描述符—— LABEL_LDT_DESC_CODEA,让我们查看一下它的代码
LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。LDTR记录局部描述符表的起始位置,与GDTR不同LDTR的内容是一个段选择子。由于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它,这个描述符就存储在GDT中,对应这个表述符也会有一个选择子,LDTR装载的就是这样一个选择子。LDTR可以在程序中通过使用lldt指令随时改变。 |