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的模式转变。
第一步
这里一个字是16bit,cpu所指的一个字是指cpu一次能处理的长度,实模式下为16bit。
lgdt指令加载全局描述符表寄存器GDTR,把GDT表的基地址和长度从内存中加载到GDTR中
第二步
这是修改寄存器CR0切换到保护模式
第三步
跳转到0地址开始执行
但里面涉及到好多东西,在实模式在采用16位段寄存器左移4位加上IP来寻址,最大寻址1M空间,但是在保护模式下,要寻址到4G,则需要GDT全局描述符表,与之相对应的是48bit的几个寄存器GDTR,
从第一步开始分析
将gdt_48的内容加载到GDTR中,先看看GDTR寄存器的形式,
![](http://img.blog.csdn.net/20150617153807811)
根据linus的注释,
第一个16bit 0x0800 加载到16位表长度
第二个16bit 512+gdt=0x200+gdt
第三个16bit 0x9
先看第一个字节为什么是0x800(2048d),原则上讲是表的长度,
看看gdt的长度,
也就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位,如下格式,
![](http://img.blog.csdn.net/20150617153958048)
所以这里我的假设是linus没有将这个gdt完全写好只是写了一部分够用即可。
可以看出第一个8字节全零,应该是保留未用,从第二个开始8字节开始填充段描述符数据。
【段限15:0】 0x07FF
【段基址15:0】 0x0000
【段基址23:16】 0x00
【段类型】 0xA
【段限19:16】 0x0
【段基址31:24】 0x00
合并段基址为 0x00000000
段限为 0x007FF
最后两个字为什么这样设置的原因,
可以先看看这时的内存分配情况
这是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指向段地址,
![](http://img.blog.csdn.net/20150617154116407)
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;
这里有个关系
![](http://img.blog.csdn.net/20150617154145748)
(图来源网络)
图中乘以8,是由于一个段描述符为8个字节。
所以由第一步的得到的GDT表,根据index选择GDT的第一个段描述符,得到:
基址为 【合并段基址为 0x00000000】
偏移 【0】
线性地址就是0。
这就是保护模式下的boot阶段寻址变换。
在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阶段寻址变换。
相关文章推荐
- Linux进程通信之信号
- linux 命令之ln
- 在CentOS上安装phpMyAdmin的教程
- linux 命令——35 ln(转)
- linux 挂载 fat32 和 ntfs格式u盘
- CentOS_6.5 postgresql 故障切换实现
- Linux pipe功能
- linux下免交互式自动生成公私钥
- linux 命令——34 du(转)
- linux及安全课程总结
- Linux3种线程同步机制的封装
- Linux用户、用户组、文件权限【命令实战】
- linux 命令——33 df(转)
- Ethernet Channel Bonding NIC Teaming on Linux Systems
- centos copy方式 eth0不见了问题解决
- linux 命令——32 gzip(转)
- linux下搭建cacti监控
- linux 文件系统之SSD
- Linux操作系统上安装Mysql数据库
- centos常用系统命令