系统在执行该行代码时已经为保护模式, jmpi 0,8会将段选择子(selector)载入cs段寄存器,并计算出逻辑地址。
段选择子的结构如下:
段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。
- index(描述符索引)部分表示对应的段描述符在描述符表(GDT或者LDT)中的位置。然后GDTR或者LDTR中存储的描述符表基址 + index * 段描述符长度 就能得到对应描述符的地址
- TI表示描述符表的类型,0=GDT,1=LDT
- RPL--请求等级,有0,1,2,3四个取值,权限从大到小
在jmpi 0,8中,段选择子的值为8。按照二进制表示即为1000,那么
TI = 0 --GDT表
RPL = 0 --内核级访问权限
INDEX = 1 --GDT表第二个元素,从0开始
而段寄存器的格式如下:
Struct SegMent{
WORD Selector段选择子;//16位selector
WORD Attribute;//16位的Attribute
DWORD Base;//32位的Base
DWORD Limit //32位的Limit
};
那么上述的selector会被加载到段寄存器cs的selector部分,而段寄存器的不可见部分相当于缓存,它会将查询到的段的信息(段基值,段限长,可读可写等属性)缓存起来,当下次仍然用同样的段选择子去查询时就不用再查表,就直接用缓存的部分,相当于提高了执行效率,当然这与该文主题没有半毛钱关系,仅仅记录。
接下来就要去GDT表中查找段的信息,那GDT表到底长什么样子?以下是GDT表中元素的排列情况,可以看到内核代码段和数据段描述符分别为GDT表的第二(index=1)和第三(index=2)个元素。
GDT表中每个元素定义如下:
图1
G表示段界限粒度,为0时表示粒度为1字节,为1时表示粒度为4KB,一般置为1
S表示是否为系统段,为1时表示系统段,为0时表示非系统段
可以看出每个段描述如占用8字节,即4个字
有了以上定义,只要知道其中的数据排列就能得出每个段的段基值和段界限,在源码中找到如下定义:
;// 全局描述符表开始处。描述符表由多个8 字节长的描述符项组成。
;// 这里给出了3 个描述符项。第1 项无用,但须存在。第2 项是系统代码段
;// 描述符(208-211 行),第3 项是系统数据段描述符(213-216 行)。每个描述符的具体
;// 含义参见列表后说明。
gdt:
dw 0,0,0,0 ;// 第1 个描述符,不用。
;// 这里在gdt 表中的偏移量为08,当加载代码段寄存器(段选择符)时,使用的是这个偏移值。
dw 07FFh ;// 8Mb - limit=2047 (2048*4096=8Mb)
dw 0000h ;// base address=0
dw 9A00h ;// code read/exec
dw 00C0h ;// granularity=4096, 386
;// 这里在gdt 表中的偏移量是10,当加载数据段寄存器(如ds 等)时,使用的是这个偏移值。
dw 07FFh ;// 8Mb - limit=2047 (2048*4096=8Mb)
dw 0000h ;// base address=0
dw 9200h ;// data read/write
dw 00C0h ;// granularity=4096, 386
那么jmpi 0,8中的index=1所指向的段描述符的内容如下:
dw 07FFh ;// 8Mb - limit=2047 (2048*4096=8Mb)
dw 0000h ;// base address=0
dw 9A00h ;// code read/exec
dw 00C0h ;// granularity=4096, 386
按照图1中进行排列,注意按照小端格式排列:
经过组合
段基值为 0x00000000 = 0
段限长为 0x007FF = 2047, 即2048 * 4KB = 8M
此处为什么要+1,应该是一种约定。另外G=1,就表示段界限单位为4KB. 另外也可以得出段界限的最大空间为
(0xFFFFF + 1) * 4KB = 4GB
那么最后可以得出jmpi 0,8所要跳转到的物理地址为
base(0) + ip(0) = 0
此地址处就是操作系统核心的head.s文件存放在内存中的地址。此处的地址需要经过MMU转换吗?系统在现在的执行阶段,其实还没有开启分页机制,那么该0其实就是真实的物理地址。这些都是由CR0寄存器决定的, 可以把它认为是操作系统的动态配置。CR0寄存器格式如下:
PE(Protection Enable)保护使能是CR0寄存器的第0位(bit 0)。当设置了PE标志时,启用保护模式;当清除PE标志时,启用实地址模式。该标志位并不直接启用分页机制,它仅启用段级别的保护。要启用分页,必须同时设置PE和PG标志。
Paging(分页)标志位是CR0寄存器的第31位。当该位被设置时(为1时),启用分页机制;当该位被清除时(为0时),禁用分页机制。当分页被禁用时,所有线性地址都被视为物理地址。也就是说没有分页机制,段机制通过段基址加偏移后就是真实物理地址,如果PE标志(寄存器CR0的第0位)未设置,PG标志则没有影响;在PE标志被清除时设置PG标志会导致通用保护异常。