一。创建并载入全局描述符表GDT
二。由实模式切换到保护模式
三。由保护模式切换到实模式
一。[[Anchor(NBE1)]]创建并载入全局描述符表GDT
在从实模式切换到保护模式之前,首先是要建立合适的全局描述符表GDT,并使用48位指针gdtdesc指向该GDT。一般情况下,需要在GDT中设置代码段和数据段的描述符。在这里的例子是以grub中的模式转换为基础的,见stage2/asm.S。
在下面的GDT中,共设置了5个段描述符,并将代码段、数据段设置成线性平坦的模式,即虚拟地址等于线性地址:
- .p2align 2;强制4字节对齐,即16位对齐
gdt:
- .word 0, 0
- .byte 0, 0, 0, 0 ;空段描述符
- .word 0xFFFF, 0 ;代码段描述符,可以看到段界限低16位为0xFFFF
- .byte 0, 0x9A, 0xCF, 0 ;第6、7字节为1100111110011010,段界限粒度G为1,段界限高四位都为1,可寻址到0xFFFFF*4K,即4G字节;段基址为0;权限为执行/读,
- .word 0xFFFF, 0 ;数据段描述符
- .byte 0, 0x92, 0xCF, 0 ;权限为读/写
- .word 0xFFFF, 0 ;16位实模式代码段描述符
- .byte 0, 0x9E, 0, 0 ;段界限粒度G为0,段界限为0xFFFF,可寻址到0xFFFF,即64K字节;段基址为0;权限为执行/读、一致码段
- .word 0xFFFF, 0 ;16位实模式数据段描述符
- .byte 0, 0x92, 0, 0 ;权限为读/写
gdtdesc:
- .word 0x27 ;GDTR界限,可以描述(0x27+1)/8=5个描述符,和上面一致
- .long gdt ;GDTR基地址,指向gdt
然后使用如下指令将GDT装载到GDTR:
在gdtdesc指针指向的结构中,低字是以字节位单位的全局描述符表段的界限,即描述符数目,高双字为描述符表段的线性基地址。
二。[[Anchor(NBE2)]]由实模式切换到保护模式
在做好准备后,从实模式切换到保护模式并不难。原则上只要把控制寄存器CR0中的PE位置1即可。本实例采用如下三条指令设置PE位:
- movl %cr0, %eax
- or $1, %eax
- mov %eax, %cr0
实际情况要比这复杂些。执行上面的三条指令后,处理器转入保护模式,但CS中的内容还是实模式下代码段的段值,而不是保护模式下代码段的选择子,所以在取指令之前得把代码段的选择子装入CS。为此,紧接着这三条指令,安排一条如下所示的段间转移指令:
- ljmp $0x8, $protcseg ;就是gdtdesc的代码段选择子,重新载入代码段
这条段间转移指令在实模式下被预取并在保护方式下被执行。利用这条段间转移指令可把保护模式下代码段的选择子装入CS,同时也刷新指令预取队列。从此真正进入保护模式。
protcseg:
- movw $0x10, %ax ;就是gdtdesc的数据段选择子,重新载入其它段
- movw %ax, %ds
- movw %ax, %es
- movw %ax, %fs
- movw %ax, %gs
- movw %ax, %ss
三。[[Anchor(NBE3)]]由保护模式切换到实模式
在80386上,从保护模式切换到实模式的过程类似于从实模式切换到保护模式。原则上只要把控制寄存器CR0中的PE位清0即可。实际上,在此之后也要安排一条段间转移指令,一方面清指令预取队列,另一方面把实模式下代码段的段值送CS。这条段间转移指令在保护方式下被预取并在实模式下被执行。
首先设置实模式段地址:
- lgdt gdtdesc
- movw $0x20, %ax ;就是gdtdesc的16位实模式数据段选择子,重新载入数据段
- movw %ax, %ds
- movw %ax, %es
- movw %ax, %fs
- movw %ax, %gs
- movw %ax, %ss
- ljmp $0x18, $tmpcseg;就是gdtdesc的16位实模式代码段选择子,重新载入代码段
然后通过设置CR0的PE位返回实模式:
tmpcseg:
- .code16
- mov %cr0, %eax
- and $11111110b, %al
- mov %eax, %cr0
然后跳转到实模式的代码地址执行:
realcseg:
- xorl %eax, %eax
- movw %ax, %ds
- movw %ax, %es
- movw %ax, %fs
- movw %ax, %gs
- movw %ax, %ss
这样就回到实模式了。
阅读(1477) | 评论(1) | 转发(0) |