Linux0.11源码学习笔记
setup.s_2
书接上文,接下来要进入模式的转换:16位-实模式---->32位-保护模式
; then we load the segment descriptors
end_move:
mov ax,#SETUPSEG ; right, forgot this at first. didn't work :-)
mov ds,ax
lidt idt_48 ; load idt with 0,0
lgdt gdt_48 ; load gdt with whatever appropriate
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
由于目前的CPU基本都是支持32位/64位模式的,处于16位实模式运行的CPU非常之少,所以为了保证兼容性,此处需要进行模式的转换
保护模式
保护模式与实模式的区别
实模式
CPU计算物理地址的方式为:段地址 << 1 + 偏移地址,用两个16位的地址合成一个20位的物理地址。而且 段地址,都是从对应的段寄存器中直接获取
保护模式
假设我们用ds数据段寄存器来举例,此时其中存储的并不是实模式下的段地址,最终的物理地址也不是 ds << 1 + 偏移地址 (16进制)
保护模式下,ds这类段寄存器中存储的是 段选择子(Seg. Selector) ,段选择子本身是一个指针,存储着段描述符的索引,通过这个索引,可以从全局描述符表(gdt)中找到一个段描述符,段描述符里存储着的才是真正的段基址,之后段基址+偏移地址 便得到了物理地址(未开启分页时就是物理地址)
段选择子结构
段描述符结构
总而言之为了找到段基址:
- 段寄存器中存储的是段选择子
- 拿着段选择子中的段描述符的索引到全局描述符表中寻找段描述符
- 从中取出段基址
那么很明显有这样一个问题:全局描述符(gdt)表去哪里找?
gdtr寄存器
那么在GDTR寄存器中是如何存储的呢,回看上述汇编指令:
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
lgdt gdt_48 ; load gdt with whatever appropriate
gdt_48:
.word 0x800 ; gdt limit=2048, 256 GDT entries
.word 512+gdt,0x9 ; gdt base = 0X9xxxx
lgdt指令用于将后面的值放入gdtr寄存器中
lgdt寄存器共48位,其中高32位存储着全局描述符表 gdt 的内存地址:
由上述汇编代码可以看出,gdt是一个标签,在编译时会转为该文件中的偏移地址,而由于setup被加载到了0x90200处,所以实际地址就是:
0x90200 + gdt
而这个gdt标签处,也就是全局描述表在内存中的真正数据了:
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
全局描述符表由多个8字节长的描述符项组成
-
.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
系统代码段描述符
在gdt表中的偏移量为0x08,加载代码段寄存器时使用这个偏移值
.word 0x07FF ; 8Mb - limit=2047 (2048*4096=8Mb).word 0x0000 ; base address=0.word 0x9200 ; data read/write.word 0x00C0 ; granularity=4096, 386
系统数据段描述符
在gdt表中的偏移量为0x10,加载代码段寄存器时使用这个偏移值
以系统代码段描述符对应其结构为
同理还有对中断描述符表的设置
idt_48:
.word 0 ; idt limit=0
.word 0,0 ; idt base=0L
lidt idt_48 ; load idt with 0,0
这个下次说