从0创建一个OS (十) 32-bit模式下的GDT
本节将学习如何使用汇编完成一个32bit模式下的全局描述符表(GDT)
关键字: GDT
目标:用汇编实现GDT结构
理论基础
之前的实模式下,我们用的寻址方法就是段地址 << 4 + 偏移地址,但是随着计算机理论的发展,人们发现,如果不在各种地址段之间加上访问权限的限制,计算机数据的安全性难以保障,恶意软件可以通过访问大量地址获得存储在计算机上的,并不属于本软件的信息.
因此人们开发出一整套机制来完成保护数据的任务,这也是"保护模式"的由来.在32-bit模式下,人们使用的寻址方法为描述符寻址,即,对于任意存储在计算机中的数据/代码,需要先寻找全局描述符表(GDT),它存有各种描述符,再通过各种描述符,寻找具体的数据所在位置.
那么怎样寻找全局描述符表(GDT)呢?找东西,用描述符,因此会有一个GDT描述符,那么如何找GDT描述符呢?难道会有GDT描述符描述符吗?不,人们人为定义了GDT描述符的位置,根据系统的不同而不同.
还有一个问题,描述符如何"描述"数据的存储位置呢?
当然是数据所在段的基地址,还有数据所在段的大小.这样可以确定我们的目标数据段,在其中寻找目标数据,而不超越该段,越界访问其它数据.
当然,为了使数据访问更加安全,人们为描述符设置了除基地址和大小之外的属性,这些属性更加精细地控制该描述符可以访问的数据类型.如访问类型(读/写/执行),描述符权限等.
以下图示说明GDT寻址过程:
还有一些额外数据
GDT最多含有8192个描述符,每个描述符8个字节.
为什么可能会经历多级描述符呢?
多级描述符可以寻址更大的地址空间.
在经历多级描述符时,除了最底层的描述符,上层的描述符都被称为描述符选择子,其作用是定位下一级描述符表.
源码
了解完了GDT,来看一下如何实现一个GDT结构为后续的寻址提供便利,本节的源码与上一节一样,暂时无法实验,给出的源码示例如何写一个GDT,实现其物理结构.
gdt_start: ; 不要认为这些标签可有可无,它们可以帮助计算GDT的size、jump范围 ; GDT的开头是8个字节的空值 dd 0x0 ; 4字节 dd 0x0 ; 4字节 ; 代码段的gdt, base = 0x00000000, length = 0xffffff ; 对于gdt中的flags,参见https://wiki.osdev.org/Global_Descriptor_Table gdt_code: dw 0xffff ; 段长度(segment length), bit0 - bit15 dw 0x0 ; 段基址(segment base), bit0 - bit15 db 0x0 ; 段基址(segment base), bit16 - bit23 db 10011010b ; Access Byte (flags)(8bits) db 11001111b ; 段长度(段长度共20bit,该字节低4bit为段长度的高4bit) ; flags标志位,共4bit,在该字节的高4bit db 0x0 ; ; 段基址(segment base), bit24 - bit31 ; 数据段的gdt, base和length和代码段一样 ; 部分标志位(flags)有所不同 gdt_data: dw 0xffff dw 0x0 db 0x0 db 10010010b db 11001111b db 0x0 gdt_end: ; GDT描述符(descriptor) gdt_descriptor: dw gdt_end - gdt_start - 1 ; 描述符中指明的GDT的大小(16bit),该大小总是比实际大小小1 dd gdt_start ; 描述符中指明的GDT的地址(32bit) ; 定义一些常数,供后续使用 CODE_SEG equ gdt_code - gdt_start DATA_SEG equ gdt_data - gdt_start
需要说明的是,GDT中的描述符选择子的Flags和Access Byte的内容如下:
Pr: Present bit. 1bit 对于所有有效的描述符(描述符选择子),Pr都必须为1.
Privl: Privilege. 2bit 包含优先级,0最高,3最低.
S: Descriptor Type. 数据段和代码段应该将该bit值1, 系统段如TSS段,应该将bit置0.
Ex: Executable bit. 如果为1, 则是代码段选择子,如果为0,则为数据段选择子.
DC: Direction bit / Conforming bit.这是个多功能bit.
对于数据段,该bit给出了数据增长方向,如果为0,那么数据段向上增长,如果为1,那么数据段向下增长(之前学过的stack就是向下增长).
对于代码段,该bit给出了代码一致性,如果为0,那么该段代码可被同级权限或更低级权限执行.如果为1,该段代码只能被Privl域设置的权限执行.
RW:可读/可写bit,对于代码段,这是一个是否可读bit,代码段不可写.对于数据段,这是一个是否可写bit,数据段一直是可读的.
Ac: Accessed,默认为0,只有在CPU访问它时,才自动设置为1
Gr: 粒度bit,如果为0,那么Limit的单位是字节,如果为1,Limit的单位是页(Page),一页为4KiB.
Sz: Size bit. 如果为0, 选择子定义16bit的保护模式,如果为1,定义32bit的保护模式,可以同时存在16bit和32bit的选择子.