您的位置:首页 > 运维架构 > Linux

linux 0.12之boot启动过程从实模式变为保护模式的一些说明

2015-06-17 12:20 951 查看
本次讨论的是linux 0.12版本,在bochs模拟x86的环境下。

在bootsect和setup代码都是执行在16位的实模式下,而head是执行在32位的保护模式下,这两模式的切换在代码中是如何实现的,这里做一个总结。

在setup将system copy到地址0后,开始了cpu的模式转变。

第一步

lidt idt_48 ! load idt with 0,0
lgdt gdt_48 ! load gdt with whatever appropriate

gdt:
.word 0,0,0,0 ! dummy
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9A00 ! code read/exec
.word 0x00C0 ! granularity=4096, 386
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
.word 0x0000 ! base address=0
.word 0x9200 ! data read/write
.word 0x00C0 ! granularity=4096, 386

idt_48:
.word 0 ! idt limit=0
.word 0,0 ! idt base=0L

gdt_48:
.word 0x800 ! gdt limit=2048, 256 GDT entries
.word 512+gdt,0x9 ! gdt base = 0X9xxxx


这里一个字是16bit,cpu所指的一个字是指cpu一次能处理的长度,实模式下为16bit。

lgdt指令加载全局描述符表寄存器GDTR,把GDT表的基地址和长度从内存中加载到GDTR中

第二步

mov ax,#0x0001 ! protected mode (PE) bit
lmsw ax ! This is it!


这是修改寄存器CR0切换到保护模式

第三步

jmpi 0,8 ! jmp offset 0 of segment 8 (cs)


跳转到0地址开始执行

但里面涉及到好多东西,在实模式在采用16位段寄存器左移4位加上IP来寻址,最大寻址1M空间,但是在保护模式下,要寻址到4G,则需要GDT全局描述符表,与之相对应的是48bit的几个寄存器GDTR,

从第一步开始分析

将gdt_48的内容加载到GDTR中,先看看GDTR寄存器的形式,



gdt_48:
.word 0x800 ! gdt limit=2048, 256 GDT entries
.word 512+gdt,0x9 ! gdt base = 0X9xxxx


根据linus的注释,

第一个16bit 0x0800 加载到16位表长度

第二个16bit 512+gdt=0x200+gdt

第三个16bit 0x9

先看第一个字节为什么是0x800(2048d),原则上讲是表的长度,

看看gdt的长度,

gdt:
.word 0,0,0,0 ! dummy                               # 4×2=8个字节
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)     # 2个字节
.word 0x0000 ! base address=0                       # 2个字节
.word 0x9A00 ! code read/exec                       # 2个字节
.word 0x00C0 ! granularity=4096, 386                # 2个字节
.word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)     # 2个字节
.word 0x0000 ! base address=0                       # 2个字节
.word 0x9200 ! data read/write                      # 2个字节
.word 0x00C0 ! granularity=4096, 386                # 2个字节


也就24个字节,按照谷歌来的说法

【由于GDT表在内存中占用2KB的内存空间,一个GDT有64位(8个字节),故有256项】

看看什么是GDT,谷歌大神说

The Global Descriptor Table or GDT is a data structure used by Intel x86-family processors starting with the 80286

是一个数据结构,有许多段描述符构成,一个段描述符为64位,如下格式,



所以这里我的假设是linus没有将这个gdt完全写好只是写了一部分够用即可。

可以看出第一个8字节全零,应该是保留未用,从第二个开始8字节开始填充段描述符数据。

【段限15:0】 0x07FF

【段基址15:0】 0x0000

【段基址23:16】 0x00

【段类型】 0xA

【段限19:16】 0x0

【段基址31:24】 0x00

合并段基址为 0x00000000

段限为 0x007FF

最后两个字为什么这样设置的原因,

可以先看看这时的内存分配情况

INITSEG = 0x9000 ! we move boot here - out of the way
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
SETUPSEG = 0x9020 ! this is the current segment


这是setup.s代码开始给出的内存情况,当前setup处于0x9020段开始的地方左移4位就是0x90200

setup处于0x90200的地方,gdt表处于setup之中,所以gdt的内存地址就是0x90200+gdt(此时作为偏移量)将这个数据拆成两个16bit数据就是

(16bit)0x0200+gdt

(16bit)0x9

原来如此,所以GDTR寄存器中的32bit线性基地址就完成

第二步是对寄存器CR0的操作

置CR0第0位,CR0的位0是启用保护(Protection Enable)标志。当设置该位时即开启了保护模式;当复位时即进入实地址模式。这个标志仅开启段级保护,而并没有启用分页机制。若要启用分页机制,那么PE和PG标志都要置位。

第三部 跳转指令,完成保护模式下的寻址

在保护模式下 还是用cs 和ip来寻址,具体如何做到的,如下分析。

在保护模式下cs这类称为段选择符,在实模式下这个寄存器左移四位直接指向段地址,而在保护模式下,就不是这样了,它是用来指向GDT的,然后通过GDT指向段地址,



jmpi 0,8 ! jmp offset 0 of segment 8 (cs)

linus注释为cs =8 ,ip=0。

8=0x08=0000_1000,所以index=0x1;T1=0;RPL=0;

这里有个关系



(图来源网络)

图中乘以8,是由于一个段描述符为8个字节。

所以由第一步的得到的GDT表,根据index选择GDT的第一个段描述符,得到:

基址为 【合并段基址为 0x00000000】

偏移 【0】

线性地址就是0。

这就是保护模式下的boot阶段寻址变换。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: