本博客参考书籍 《操作系统 真象还原》 作者:郑钢
在以前就知道实模式,虚拟模式,保护模式。但就知道个名字。今天实模式入门还是挺开心的。这里面东西很多。咱们重点谈段描述符和全局描述符表。
1、段描述符
段描述符是全局描述符表的组成部分。顾名思义,段描述符是描述段的相关信息。我们现在还没有看到具体的段描述符,但是我们可以简单的预测一些段描述符的相关信息。首先为了防止越界,我们要加入界限的相关信息。为了访问更大的内存,我们需要更多位来表示段基址。更多的位数,我们可以将他们放入内存…
具体的段描述符格式见下图。
每一位详细的介绍,见书上。
2、全局描述符表
由于段描述符已经很长了。如果还是使用段地址:偏移地址,则要花很长的寄存器来存储段描述符。
那怎么办?重点来了。
实模式下,段寄存器存储的不再是段地址,而是选择子。
段选择子,类似于数组的下表,通过这个下表便可以在全局描述符找到段的信息。
段描述符,本质上,是段的“身份证”。选择子的作用是确定段描述符。
全局描述符表GDT(由于我现在还不清楚局部描述符表LDT,所以暂时不考虑它。),存放在内存中。
CPU通过寄存器GDTR来存储全局描述符表的位置和限界。
通过lgdt 指令来赋值GDTR。
3、进入实模式
; 打开A20地址线
in al, 0x92
or al, 00000010B
out 0x92, al
; 加载gdt
lgdt [gdt_ptr]
; cr0第0位置1
mov eax, cr0
or eax, 0x00000001
mov cr0, eax
4、代码
4.1 boot.inc
;---------------------------loader和kernel-------------
LOADER_BASE_ADDR equ 0x900 ;loader放入内存的位置
LOADER_START_SECTOR equ 0x2 ;loader从硬盘读取loader的位置。即loader在硬盘的存放位置
; gdt描述符属性
; 段描述符高23位,表示段界限的粒度为4KB.段界限*粒度=2^20*4k=4G
DESC_G_4K equ 100000000000000000000000b
; D/B为,1表示运行在32位模式下.(地址和操作数是32位)
DESC_D_32 equ 10000000000000000000000b
; 高21位,如果为1表示为64位代码段,目前我们都是在32位模式下操作,故为零
DESC_L equ 0000000000000000000000b
; 没有明确的用途,取值随意
DESC_AVL equ 000000000000000000000b
; 第二部分段界限值,由于采用了32位平坦模型,所以段界限为(4GB / 4KB) - 1 = 0xFFFFF,故为全1
DESC_LIMIT_CODE2 equ 11110000000000000000b
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2
; 书中取全零,怀疑是错误的,因为保护模式的基地址是0xb8000,所以最后8位应该是b,而不是0
;书中应该是写错了
DESC_LIMIT_VIDEO2 equ 00000000000000000000000000001011b
DESC_P equ 1000000000000000b ;在内存中,P置1
DESC_DPL_0 equ 000000000000000b ;下面几个是特权级,分别是0,1,2,3
DESC_DPL_1 equ 010000000000000b
DESC_DPL_2 equ 100000000000000b
DESC_DPL_3 equ 110000000000000b
DESC_S_CODE equ 1000000000000b ;S为1是非系统段,S为0是系统段
DESC_S_DATA equ DESC_S_CODE
DESC_S_sys equ 0000000000000b
;x=1,c=0,r=0,a=0 代码段可执行,非一致性,不可读,已访问位清零
DESC_TYPE_CODE equ 100000000000b
;x=0,c=0,r=1,a=0 数据段不可执行,可读写数据段,已访问位清零
DESC_TYPE_DATA equ 001000000000b
; 代码段描述符的高32位表示,其中(0x00 << 24表示最高8位的段基址值,由于我们采用的是平坦模型,故基址为零),后面唯一可变的就是段界限值
DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00
;数据段描述符高32位
DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00
;显存段描述符高32位
DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00
; 选择字属性
RPL0 equ 00b;低两位的请求特权
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b;0代表GDT,1代表LDT
TI_LDT equ 100b
4.2 loader.asm
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start
; 这里其实就是GDT的起始地址,第一个描述符为空
GDT_BASE: dd 0x00000000
dd 0x00000000
; 代码段描述符,一个dd为4字节,段描述符为8字节,上面为低4字节
; 段界限全是f,所以能最大访问4G
CODE_DESC: dd 0x0000FFFF
dd DESC_CODE_HIGH4
; 栈段描述符,和数据段共用
DATA_STACK_DESC: dd 0x0000FFFF
dd DESC_DATA_HIGH4
; 显卡段,非平坦
; 显存的地址范围是:0xb8000~0xbffff。b在高四个字节中哦,在头文件中已经设置过了。
; 低四个字节从b开始。按照粒度为4k划分,最大限度为7
VIDEO_DESC: dd 0x80000007
dd DESC_VIDEO_HIGH4
GDT_SIZE equ $ - GDT_BASE
GDT_LIMIT equ GDT_SIZE - 1
times 120 dd 0
SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 ;分别是三个选择子
SELECTOR_DATA equ (0x0002 << 3) + TI_GDT + RPL0
SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0
gdt_ptr dw GDT_LIMIT ;低两个字节表GDT界限
dd GDT_BASE ;高四个字节表GDT在内存的起始地址:0+start=0x900
loadermsg db '2 loader in real.'
loader_start:
; 调用10h号中断显示字符串
mov sp, LOADER_BASE_ADDR
mov bp, loadermsg
; 字符串长度
mov cx, 17
; 子功能号以及显示方式
mov ax, 0x1301
; 页号:0, 蓝底粉红字
mov bx, 0x001f
mov dx, 0x1800
int 0x10
; 打开A20地址线
in al, 0x92
or al, 00000010B
out 0x92, al
; 加载gdt
lgdt [gdt_ptr]
; cr0第0位置1
mov eax, cr0
or eax, 0x00000001
mov cr0, eax
; 刷新流水线
jmp dword SELECTOR_CODE:p_mode_start
[bits 32]
p_mode_start:
mov ax, SELECTOR_DATA
mov ds, ax
mov es, ax
mov ss, ax
mov esp, LOADER_STACK_TOP
mov ax, SELECTOR_VIDEO
mov gs, ax
mov byte [gs:160], 'P'
; 与书中不同,这里使用蓝底白字
mov byte [gs:161], 0x1f
jmp $
5、结果
6、tips
1、
我们来分析下。
显存的地址范围是:0xb8000~0xbffff。
段描述符中,段基址分为三部分:31~24, 23~16,15~0。
所以:DESC_LIMIT_VIDEO2 equ 00000000000000000000000000001011b ;第二部分是左边这样滴。
前两部分在高四个字节。最后一部分,在低四个字节的最高一个字节中。
VIDEO_DESC: dd 0x80000007 ;limit=(0xbffff-0xb8000)/4k=7
dd DESC_VIDEO_HIGH4
参考:https://github.com/da1234cao/tiny-os
2、
gdt_ptr dw GDT_LIMIT ;低两个字节表GDT界限
dd GDT_BASE ;高四个字节表GDT在内存的起始地址:0+start=0x900
3、
info gdt ;查看GDT的相关信息
creg ;查看控制寄存器的命令
4、
选择结构对流水线有很大影响。
5、
检查是否越界
注:这一章,最难理解的是代码,文章的截图来自书本。详细内容见书本。