Linux0.11_setup.s_2

120 阅读4分钟

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)中找到一个段描述符,段描述符里存储着的才是真正的段基址,之后段基址+偏移地址 便得到了物理地址(未开启分页时就是物理地址)

微信截图_20220609163312.png

段选择子结构

微信截图_20220609162907.png

段描述符结构

微信截图_20220609163123.png

总而言之为了找到段基址

  1. 段寄存器中存储的是段选择子
  2. 拿着段选择子中的段描述符的索引到全局描述符表中寻找段描述符
  3. 从中取出段基址

那么很明显有这样一个问题:全局描述符(gdt)表去哪里找?

gdtr寄存器

微信截图_20220609165008.png

那么在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寄存器中

微信截图_20220609165703.png

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,加载代码段寄存器时使用这个偏移值

系统代码段描述符对应其结构为

微信截图_20220609175309.png

同理还有对中断描述符表的设置

idt_48:
    .word   0           ; idt limit=0
    .word   0,0         ; idt base=0L
    
lidt    idt_48      ; load idt with 0,0

这个下次说