GDT(全居描述符表)和LDT(局部描述符表)

GDT的由来:



在Protected Mode下,一个重要的必不可少的数据结构就是GDT(Global Descriptor Table)。
为什么要有GDT?我们首先考虑一下在Real Mode下的编程模型:
在Real Mode下,我们对一个内存地址的访问是通过Segment:Offset的方式来进行的,其中Segment是一个段的Base Address,一个Segment的最大长度是64 KB,这是16-bit系统所能表示的最大长度。而Offset则是相对于此Segment Base Address的偏移量。Base Address+Offset就是一个内存绝对地址。由此,我们可以看出,一个段具备两个因素:Base Address和Limit(段的最大长度),而对一个内存地址的访问,则是需要指出:使用哪个段?以及相对于这个段Base Address的Offset,这个Offset应该小于此段的Limit。当然对于16-bit系统,Limit不要指定,默认为最大长度64KB,而 16-bit的Offset也永远不可能大于此Limit。我们在实际编程的时候,使用16-bit段寄存器CS(Code Segment),DS(Data Segment),SS(Stack Segment)来指定Segment,CPU将段积存器中的数值向左偏移4-bit,放到20-bit的地址线上就成为20-bit的Base Address。
到了Protected Mode,内存的管理模式分为两种,段模式和页模式,其中页模式也是基于段模式的。也就是说,Protected Mode的内存管理模式事实上是:纯段模式和段页式。进一步说,段模式是必不可少的,而页模式则是可选的——如果使用页模式,则是段页式;否则这是纯段模式。
既然是这样,我们就先不去考虑页模式。对于段模式来讲,访问一个内存地址仍然使用Segment:Offset的方式,这是很自然的。由于 Protected Mode运行在32-bit系统上,那么Segment的两个因素:Base Address和Limit也都是32位的。IA-32允许将一个段的Base Address设为32-bit所能表示的任何值(Limit则可以被设为32-bit所能表示的,以2^12为倍数的任何指),而不像Real Mode下,一个段的Base Address只能是16的倍数(因为其低4-bit是通过左移运算得来的,只能为0,从而达到使用16-bit段寄存器表示20-bit Base Address的目的),而一个段的Limit只能为固定值64 KB。另外,Protected Mode,顾名思义,又为段模式提供了保护机制,也就说一个段的描述符需要规定对自身的访问权限(Access)。所以,在Protected Mode下,对一个段的描述则包括3方面因素:【Base Address, Limit, Access】,它们加在一起被放在一个64-bit长的数据结构中,被称为段描述符。这种情况下,如果我们直接通过一个64-bit段描述符来引用一个段的时候,就必须使用一个64-bit长的段寄存器装入这个段描述符。但Intel为了保持向后兼容,将段寄存器仍然规定为16-bit(尽管每个段寄存器事实上有一个64-bit长的不可见部分,但对于程序员来说,段寄存器就是16-bit的),那么很明显,我们无法通过16-bit长度的段寄存器来直接引用64-bit的段描述符。
怎么办?解决的方法就是把这些长度为64-bit的段描述符放入一个数组中,而将段寄存器中的值作为下标索引来间接引用(事实上,是将段寄存器中的高13 -bit的内容作为索引)。这个全局的数组就是GDT。事实上,在GDT中存放的不仅仅是段描述符,还有其它描述符,它们都是64-bit长。GDT是Protected Mode所必须的数据结构,也是唯一的。另外,正像它的名字(Global Descriptor Table)所揭示的,它是全局可见的,对任何一个任务而言都是这样。


GDT的构成:


GDT的结构图如下:(GDT表相当于一个64bit的数组)


1、G:
(1)、G=0时,段限长的20位为实际段限长,最大限长为2^20=1MB
(2)、G=1时,则实际段限长为20位段限长乘以2^12=4KB,最大限长达到4GB

2、D/B:
当描述符指向的是可执行代码段时,这一位叫做D位,D=1使用32位地址和32/8位操作数,D=0使用16位地址和16/8位操作数。如果指向的是向下扩展的数据段,这一位叫做B位,B=1时段的上界为4GB,B=0时段的上界为64KB。如果指向的是堆栈段,这一位叫做B位,B=1使用32位操作数,堆栈指针用ESP,B=0时使用16位操作数,堆栈指针用SP。

3、DPL:特权级,0为最高特权级,3为最低,表示访问该段时CPU所需处于的最低特权级

4、type : 类型
(1)、type<8时:数据段


(2)、type>=8时:代码段



GDTR是什么?

GDT可以被放在内存的任何位置,那么当程序员通过段寄存器来引用一个段描述符时,CPU必须知道GDT的入口,也就是基地址放在哪里,所以Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此寄存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。

LDT是什么?

除了GDT之外,IA-32还允许程序员构建与GDT类似的数据结构,它们被称作LDT(Local Descriptor Table,局部描述符表),但与GDT不同的是,LDT在系统中可以存在多个,并且从LDT的名字可以得知,LDT不是全局可见的,它们只对引用它们的任务可见,每个任务最多可以拥有一个LDT。另外,每一个LDT自身作为一个段存在,它们的段描述符被放在GDT中。
 LDT只是一个可选的数据结构,你完全可以不用它。使用它或许可以带来一些方便性,但同时也带来复杂性,如果你想让你的OS内核保持简洁性,以及可移植性,则最好不要使用它。


LDTR是什么?


IA-32为LDT的入口地址也提供了一个寄存器LDTR,因为在任何时刻只能有一个任务在运行,所以LDT寄存器全局也只需要有一个。如果一个任务拥有自身的LDT,那么当它需要引用自身的LDT时,它需要通过lldt指令将其LDT的段描述符装入此寄存器。lldt指令与lgdt指令不同的时,lgdt指令的操作数是一个32-bit的内存地址,这个内存地址处存放的是一个32-bit GDT的入口地址,以及16-bit的GDT Limit。而lldt指令的操作数是一个16-bit的选择子,这个选择子主要内容是:被装入的LDT的段描述符在GDT中的索引值。


至此,我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。如图:

例如:如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h。

1. 首先需要装载LDTR使它指向LDT2:使用指令lldt将Select2装载到LDTR。
2. 通过逻辑地址(SEL:OFFSET)访问时SEL的index=3代表选择第三个描述符;TI=1代表选择子是在LDT选择,此时LDTR指向的是LDT2,所以是在LDT2中选择,此时的SEL值为1C h(二进制为11 1 00b),OFFSET=12345678h。逻辑地址为1C:12345678h。
3. 由SEL选择出描述符,由描述符中的基址(Base)加上OFFSET可得到线性地址,例如基址是11111111h,则线性地址=11111111h+12345678h=23456789h。
4. 此时若再想访问LDT1中的第三个描述符,只要使用lldt指令将选择子Selector 1装入再执行2、3两步就可以了(因为此时LDTR又指向了LDT1)。

由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表则可以将每个进程的程序段、数据段、堆栈段封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问。


段选择子是什么?

引用GDT和LDT中的段描述符所描述的段,是通过一个16-bit的数据结构来实现的,这个数据结构叫做Segment Selector——段选择子。它的高13位作为被引用的段描述符在GDT/LDT中的下标索引,bit 2用来指定被引用段描述符被放在GDT中还是到LDT中,bit 0和bit 1是RPL——请求特权等级,被用来做保护目的。如图所示:


前面所讨论的装入段寄存器中作为GDT/LDT索引的就是Segment Selector,当需要引用一个内存地址时,使用的仍然是Segment:Offset模式,具体操作是:在相应的段寄存器装入Segment Selector,按照这个Segment Selector可以到GDT或LDT中找到相应的Segment Descriptor,这个Segment Descriptor中记录了此段的Base Address,然后加上Offset,就得到了最后的内存地址。

段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。它的index(描述符索引)部分表示所需要的段的描述符在描述符表的位置,由这个位置再根据在GDTR中存储的描述符表基址就可以找到相应的描述符。然后用描述符表中的段基址加上逻辑地址(SEL:OFFSET)的OFFSET就可以转换成线性地址,段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代表选择子是在LDT选择。请求特权级(RPL)则代表选择子的特权级,共有4个特权级(0级、1级、2级、3级)。
关于特权级的说明:任务中的每一个段都有一个特定的级别。每当一个程序试图访问某一个段时,就将该程序所拥有的特权级与要访问的特权级进行比较,以决定能否访问该段。系统约定,CPU只能访问同一特权级或级别较低特权级的段。
例如给出逻辑地址:21h:12345678h 转换为线性地址的步骤如下:
(1)、选择子SEL=21h=0000000000100 0 01b 它代表的意思是:选择子的index=4即选择GDT中的第4个描述符;TI=0代表选择子是在GDT选择;最后的01代表特权级RPL=1
(2)、OFFSET=12345678h若此时GDT第四个描述符中描述的段基址(Base)为11111111h,则线性地址=11111111h+12345678h=23456789h

关系:

计算机由实模式进入到保护模式要加载gdt,保护模式下的段寄存器由16位的选择器与64位的段描述符寄存器构成。
段描述符寄存器: 存储段描述符;
选择器:存储段描述符的索引;
原先实模式下的各个段寄存器作为保护模式下的段选择器,80486中有6个(即CS,SS,DS,ES,FS,GS)16位的段寄存器

GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此积存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界限。
48bit的GDTR的结构:

由GDTR访问GDT是通过“段选择子”(实模式下的段寄存器)来完成的,即:
GDTR(48bit)+Segment Selector(由实模式下的段寄存器充当,16bit)= GDT中的某一项 (64bit)


Tips:
(1)、GDT中的所有描述符(除0项外,包括LDT描述符这种类型的描述符)都指定一个段,GDT中的LDT描述符指定的是一个特殊的段, 这个段中只能存放LDT表,之所以说GDT中一个描述符是一个LDT段的描述符,是因为这个描述符中有属性说明这是一个LDT段.
(2)、
GDTR是一个48位的全局描述符寄存器,高32位存放GDT的基址,低16位存放GDT限长。
LDTR是一个16位的局部描述符寄存器,高13位存放LDT在GDT中的索引值。
GDT类似于一个 “数组” ,“数组元素” 可以是段描述符,也可以是LDT描述符。

例如:
若给定一个逻辑地址是 a:b ,根据逻辑地址的a(段选择符)的T1位确定是选择GDT还是LDT。
a、若是T1位选择GDT,根据GDTR找到GDT的基址,根据a的 3~15位确定它的段描述符X在GDT中的位置(GDTR即基址+a的3-15bit即相对位置):确定段描述符X,再根据段描述符提取出其中包含的段基址信息,段基址+b(段内偏移),最终确定线性地址。
2)若是T1位选择LDT,根据GDTR找到GDT的基址,根据LDTR的高13位确定它的LDTX描述符在GDT中的位置(GDTR基址+LDTR13bit即相对位置):确定LDTX描述符。LDTX描述符可以确定LDT的基址(LDTX描述符确定LDT表在内存中的起始位置),再根据段选择符a确定的相对位置,可以确定LDT中的私有段描述符Y。接下来同上面的:再根据段描述符提取出其中包含的段基址信息,段基址+b(段内偏移),最终确定线性地址。

GDT中包含的段描述符X和LDT中包含的私有段描述符Y,所占空间相同。
GDT中包含的段描述符X和GDT中包含的LDT描述符,所占用空间相同。

结论是:LDT不包含在GDT中。GDT中只是包含了LDT描述符(一个指向LDT起始地址的指针)。



  • 19
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
微机课后题目答案啊 微机接口技术练习题解 第1章 绪论 1. 计算机分那几类?各有什么特点? 答:传统上分为三类:大型主机、小型机、微型机。大型主机一般为高性能的并行处理系统, 存储容量大,事物处理能力强,可为众多用户提供服务。小型机具有一定的数据处理能力,提供一定用户规模的信息服务,作为部门的信息服务中心。微型机一般指在办公室或家庭的桌面或可移动的计算系统,体积小、价格低、具有工业化标准体系结构,兼容性好。 2. 简述微处理器、微计算机及微计算机系统三个术语的内涵。 答:微处理器是微计算机系统的核心硬件部件,对系统的性能起决定性的影响。微计算机包括微处理器、存储器、I/O接口电路及系统总线。微计算机系统是在微计算机的基础上配上相应的外部设备和各种软件,形成一个完整的、独立的信息处理系统。 3. 80X86微处理器有几代?各代的名称是什么? 答:从体系结构上可分为3代: ◆ 8080/8085:8位机。 ◆ 8086/8088/80286:16位机。 ◆ 80386/80486:32位机。 第2章 微处理器结构及微计算机的组成 1. 8086是多少位的微处理器?为什么? 答:8086是16位的微处理器,其内部数据通路为16位,对外的数据总线也是16位。 2. EU与BIU各自的功能是什么?如何协同工作? 答:EU是执行部件,主要的功能是执行指令。BIU是总线接口部件,与片外存储器及I/O接口电路传输数据。EU经过BIU进行片外操作数的访问,BIU为EU提供将要执行的指令。EU与BIU可分别独立工作,当EU不需BIU提供服务时,BIU可进行填充指令队列的操作。 3. 8086/8088与其前一代微处理器8085相比,内部操作有什么改进? 答:8085为8位机,在执行指令过程中,取指令与执行执令都是串行的。8086/8088由于内部有EU和BIU两个功能部件,可重叠操作,提高了处理器的性能。 4. 8086/8088微处理器内部有那些寄存器,它们的主要作用是什么? 答:执行部件有8个16位寄存器,AX、BX、CX、DX、SP、BP、DI、SI。AX、BX、CX、DX一般作为通用数据寄存器。SP为堆栈指针存器,BP、DI、SI在间接寻址时作为地址寄存器或变址寄存器。总线接口部件设有段寄存器CS、DS、SS、ES和指令指针寄存器IP。段寄存器存放段地址,与偏移地址共同形成存储器的物理地址。IP的内容为下一条将要执行指令的偏移地址,与CS共同形成下一条指令的物理地址。 5. 8086对存储器的管理为什么采用分段的办法? 答:8086是一个16位的结构,采用分段管理办法可形成超过16位的存储器物理地址,扩大对存储器的寻址范围 (1MB,20位地址)。若不用分段方法,16位地址只能寻址64KB空间。 6.在8086中,逻辑地址、偏移地址、物理地址分别指的是什么?具体说明。 答:逻辑地址是在程序中对存储器地址的一种示方法,由段地址和段内偏移地址两部分组成,如1234H:0088H。偏移地址是指段内某个存储单元相对该段首地址的差值,是一个16位的二进制代码。物理地址是8086芯片引线送出的20位地址码,用来指出一个特定的存储单元。 7.给定一个存放数据的内存单元的偏移地址是20C0H,(DS)=0C00EH,求出该内存单元的物理地址。 答:物理地址:320F8H。 8.8086/8088为什么采用地址/数据引线复用技术? 答:考虑到芯片成本,8086/8088采用40条引线的封装结构。40条引线引出8086/8088的所有信号是不够用的,采用地址/数据线复用引线方法可以解决这一矛盾,从逻辑角度,地址与数据信号不会同时出现,二者可以分时复用同一组引线。 9. 8086与8088的主要区别是什么? 答:8086有16条数据信号引线,8088只有8条;8086片内指令预取缓冲器深度为6字节,8088只有4字节。 10. 怎样确定8086的最大或最小工作模式?最大、最小模式产生控制信号的方法有何不同 答:引线MN/MX#的逻辑状态决定8086的工作模式,MN/MX#引线接高电平,8086被设定为最小模式,MN/MX#引线接低电平,8086被设定为最大模式。 最小模式下的控制信号由相关引线直接提供;最大模式下控制信号由8288专用芯片译码后提供,8288的输入为8086的S2#~S0#三条状态信号引线提供。 11. 8086被复位以后,有关寄存器的状态是什么?微处理器从何处开始执行程序? 答:标志寄存器、IP、DS、SS、ES和指令队列置0,CS置全1。处理器从FFFFOH存储单元取指令并开始执行。 12.8086基本总线周期是如何组成的?各状态中完成什么基本操作? 答:基本总线周期由4个时钟(CLK)周期组成,按时间顺序定义为T1、T2、T3、T4。在T1期间8086发出访问目的地的地址信号和地址锁存选通信号ALE;T2期间发出读写命令信号RD#、WR#及其它相关信号;T3期间完成数据的访问;T4结束该总线周期。 13.结合8086最小模式下总线操作时序图,说明ALE、M/IO#、DT/R#、RD#、READY信号的功能。 答:ALE为外部地址锁存器的选通脉冲,在T1期间输出;M/IO#确定总线操作的对象是存储器还是I/O接口电路,在T1输出;DT/R#为数据总线缓冲器的方向控制信号,在T1输出;RD#为读命令信号;在T2输出;READY信号为存储器或I/O接口“准备好”信号,在T3期间给出,否则8086要在T3与T4间插入Tw等待状态。 14.8086中断分哪两类?8086可处理多少种中断? 答:8086中断可分为硬件中断和软件中断两类。8086可处理256种类型的中断。 15.8086可屏蔽中断请求输入线是什么?“可屏蔽”的涵义是什么? 答:可屏蔽中断请求输入线为INTR;“可屏蔽”是指该中断请求可经软件清除标志寄存器中IF位而被禁止。 16.8086的中断向量如何组成?作用是什么? 答:把内存0段中0~3FFH区域作为中断向量的专用存储区。该区域存放256种中断的处理程序的入口地址,每个入口地址占用4个存储单元,分别存放入口的段地址与偏移地址。 17.8086如何响应一个可屏蔽中断请求?简述响应过程。 答:当8086收到INTR的高电平信号时,在当前指令执行完且IF=1的条件下,8086在两个总线周期中分别发出INTA#有效信号;在第二个INTA#期间,8086收到中断源发来的一字节中断类型码;8086完成保护现场的操作,CS、IP内容进入堆栈,请除IF、TF;8086将类型码乘4后得到中断向量的入口地址,从此地址开始读取4字节的中断处理程序的入口地址,8086从此地址开始执行程序,完成了INTR中断请求的响应过程。 18.什么是总线请求?8086在最小工作模式下,有关总线请求的信号引脚是什么? 答:系统中若存在多个可控制总线的主模块时,其中之一若要使用总线进行数据传输时,需向系统请求总线的控制权,这就是一个总线请求的过程。8086在最小工作模式下有关总线请求的信号引脚是HOLD与HLDA。 19.简述在最小工作模式下,8086如何响应一个总线请求? 答:外部总线主控模块经HOLD引线向8086发出总线请求信号;8086在每个时钟周期的上升沿采样HOLD引线;若发现HOLD=1则在当前总线周期结束时(T4结束)发出总线请求的响应信号HLDA;8086使地址、数据及控制总线进入高阻状态,让出总线控制权,完成响应过程。 20.在基于8086的微计算机系统中,存储器是如何组织的?是如何与处理器总线连接的? BHE#信号起什么作用? 答:8086为16位处理器,可访问1M字节的存储器空间;1M字节的存储器分为两个512K字节的存储体,命名为偶字节体和奇字节体;偶体的数据线连接D7~D0,“体选”信号接地址线A0;奇体的数据线连接D15~D8,“体选”信号接BHE#信号;BHE#信号有效时允许访问奇体中的高字节存储单元,实现8086的低字节访问、高字节访问及字访问。 21.“80386是一个32位微处理器”,这句话的涵义主要指的是什么? 答:指80386的数据总线为32位,片内寄存器和主要功能部件均为32位,片内数据通路为32位。 22.80X86系列微处理器采取与先前的微处理器兼容的技术路线,有什么好处?有什么不足? 答:好处是先前开发的软件可以在新处理器组成的系统中运行,保护了软件投资。缺点是处理器的结构发展受到兼容的约束,为了保持兼容性增加了硅资源的开销,增加了结构的复杂性。 23.80386内部结构由哪几部分组成?简述各部分的作用。 答:80386内部结构由执行部件(EU)、存储器管理部件(MMU)和总线接口部件(BIU)三部分组成。EU包括指令预取部件、指令译码部件、控制部件、运算部件及保护检测部件,主要功能是执行指令。存储器管理部件包括分段部件、分页部件,实现对存储器的分段分页式的管理,将逻辑地址转换成物理地址。总线接口部件作用是进行片外访问:对存储器及I/O接口的访问、预取指令;另外的作用是进行总线及中断请求的控制 24.80386有几种存储器管理模式?都是什么? 答:80386有三种存储器管理模式,分别是实地址方式、保护方式和虚拟8086方式 25.在不同的存储器管理模式下,80386的段寄存器的作用是什么? 答:在实地址方式下,段寄存器与8086相同,存放段基地址。在保护方式下,每个段寄存器还有一个对应的64位段描述符寄存器,段寄存器作为选择器存放选择符。在虚拟8086方式下,段寄存器的作用与8086相同。 26.试说明虚拟存储器的涵义,它与物理存储器有什么区别?80386虚拟地址空间有多大? 答:虚拟存储器是程序员面对的一个巨大的、可寻址的存储空间,这个空间是内存与外存联合形成的,在操作系统的管理下,程序可象访问内存一样去访问外存而获得所需数据。物理存储器是指机器实际拥有的内存储器,不包括外存。80386的虚拟地址空间为64TB大。 27.试说明描述符的分类及各描述符的作用。 答:描述符分为三类:存储器段描述符、系统段描述符、门描述符。存储器段描述符由8字节组成,它用来说明一个段中保存信息的情况。32位段基地址和20位段界限值定位了该段在存储空间中的位置,其它有关位决定访问权限及段的长度单位。系统段描述符与存储器段描述符大多数字节段相同,有关访问权及属性字节段有些不同。门描述符用来改变程序的特权级别、切换任务的执行以及指出中断服务程序的入口。 28.描述符的作用是什么?有几类描述符? 答:描述符顺序存放一系列描述符描述符定义了在80386系统中被使用的全部存储器段。有3类描述符,即全局描述符局部描述符及中断描述符。 29.80386的分段部件是如何将逻辑地址变为线性地址的? 答:分段部件根据段选择符从全局描述符局部描述符中取出对应的段描述符。把段描述符32位段基地址与逻辑地址中的32位偏移量相加就形成了线性地址。 30.80386中如何把线性地址变为物理地址? 答:分段部件形成的32位线性地址中高10位作为寻址页目录的偏移量,与控制寄存器CR3中页目录基地址共同形成一个32位的地址指向页中的一个页项,即为一个页面描述符。该页面项中高20位作为页面基地址,线性地址的低12位为偏移量,相加后形成指向某一存储单元的32位物理地址。若禁止分页功能,线性地址就是物理地址。 31.80386对中断如何分类? 答:80386把中断分为外部中断和内部中断两大类,外部中断经NMI和INTR引线输入请求信号。内部中断也叫内部异常中断,分为陷阱中断、内部故障异常中断、异常终止中断。 32.80386在保护方式下中断描述符与8086的中断向量有什么不同? 答:8086工作在实地址方式,向量是在存储器的0段中最低1024字节内存中。80386在保护方式下要通过中断描述符中的描述符访问虚拟空间的中断向量,中断描述符的位置不是固定的,要由IDTR寄存器实现在虚拟空间的定位。 33.简述80386在保护方式下的中断处理过程。 答:80386响应中断后,接收由中断源提供的类型码并将其乘8,与IDTR寄存器中基地址相加,指出中断描述符的位置,读出中断描述符,依其中的段选择符及条件决定从两个描述符LDTGDT中的一个得到段描述符,形成中断服务程序入口所在存储器单元的线性地址。 第3章 8086指令系统及寻址方式
目录 第一篇 x86 基础 第1 章数与数据类型2 1.1 数 2 1.1.1 数字 2 1.1.2 二进制数 3 1.1.3 二进制数的排列 3 1.1.4 十六进制数 5 1.1.5 八进制数与十进制数 5 1.2 数据类型 6 1.2.1 integer 数 6 1.2.2 floating-point 数. 9 1.2.3 real number(实数)与NaN(not a number) . 11 1.2.4 unsupported 编码值 14 1.2.5 浮点数精度的转换 15 1.2.6 浮点数的溢出 17 1.2.7 BCD 码 20 1.2.8 SIMD 数据 21 第2 章 x86/x64 编程基础 23 2.1 选择编译器 23 2.2 机器语言 24 2.3 Hello world 25 2.3.1 使用寄存器传递参数 26 2.3.2 调用过程 27 2.3.3 定义变量 27 2.4 16 位编程、32 位编程,以及64 位编程 28 2.4.1 通用寄存器 28 2.4.2 操作数大小 30 2.4.2 64 位模式下的内存地址 30 2.4.4 内存寻址模式 31 2.4.5 内存寻址范围 34 2.4.6 使用的指令限制 34 2.5 编程基础 34 2.5.1 操作数寻址 35 2.5.2 传送数据指令 39 2.5.3 位操作指令 45 2.5.4 算术指令 47 2.5.5 CALL 与RET 指令 48 2.5.6 跳转指令 48 2.6 编辑与编译、运行 48 第 3 章编写本书的实验例子 50 3.1 实验的运行环境 50 3.2 生成空白的映像文件 52 3.2.1 使用nasm 编译器生成 52 3.2.2 使用bximage 工具 52 3.3 设置bochs 配置文件. 53 3.4 源代码的基本结构 54 3.5 编译源代码55 3.6 映像文件内的组织 55 3.7 使用merge 工具 56 3.7.1 merge 的配置文件 57 3.7.2 执行merge 命令 57 3.8 使用U 盘启动真实机器 58 3.8.1 使用merge 工具写U 盘 58 3.8.2 使用hex 编辑软件写U 盘 59 3.9 编写boot 代码 60 3.9.1 LBA 转换为CHS 62 3.9.2 测试是否支持int 13h 扩展功能 63 3.9.3 使用int 13h 扩展读磁盘 64 3.9.4 最后看看load_module() 64 3.10 总结 66 第4 章处理器的身份 67 4.1 测试是否支持CPUID 指令 67 4.2 CPUID 指令的术语及达 68 4.3 基本信息与扩展信息 68 4.4 处理器的型号(family,model 与stepping) 72 4.5 最大的物理地址和线性地址 73 4.6 处理器扩展状态信息74 4.6.1 探测Processor Extended State 子叶 75 4.6.2 Processor Extended State 子叶所需内存size 76 4.6.3 Processor Extended State 的保存 77 4.6.4 Processor Extended State 的恢复 78 4.7 处理器的特性 78 4.8 处理器的Cache 与TLB 信息 80 4.9 MONITOR/MWAIT 信息 83 4.10 处理器的long mode 84 第 5 章了解 Flags 85 5.1 Eflags 中的状态标志位 86 5.1.1 signed 数的运算 86 5.1.2 unsigned 数的运算 89 5.2 IOPL 标志位 90 5.3 TF 标志与RF 标志 93 5.4 NT 标志 95 5.5 AC 标志 96 5.6 VM 标志 98 5.7 eflags 寄存器的其他事项 99 第 6 章处理器的控制寄存器 101 6.1 CR8 102 6.2 CR3 103 6.3 CR0 104 6.3.1 保护模式位PE 104 6.3.2 x87 FPU 单元的执行环境 104 6.3.3 CR0.PG 控制位 108 6.3.4 CR0.CD 与CR0.NW 控制位 108 6.3.5 CR0.WP 控制位 110 6.3.6 CR0.AM 控制位 110 6.4 CR4 110 6.4.1 CR4.TSD 与CR4.PCE 控制位 110 6.4.2 CR4.DE 与CR4.MCD 控制位 111 6.4.3 CR4.OSFXSR 控制位 111 6.4.4 CR4.VMXE 与CR4.SMXE 控制位 111 6.4.5 CR4.PCIDE 与CR4.SMEP 控制位 112 6.4.6 CR4.OSXSAVE 控制位 113 6.4.7 CR4 中关于页的控制位 113 6.5 EFER 扩展功能寄存器 114 第 7 章 MSR. 116 7.1 MSR 的使用 116 7.2 MTRR 117 7.2.1 Fixed-range 区域的映射 118 7.2.2 MTRR 的功能寄存器 120 7.3 MSR 中对特殊指令的支持 124 7.3.1 支持sysenter/sysexit 指令的MSR 125 7.3.2 支持syscall/sysret 指令的MSR 126 7.3.3 支持swapgs 指令的MSR 127 7.3.4 支持monitor/mwait 指令的MSR 128 7.4 提供processor feature 管理 129 7.5 其他未列出来的MSR 129 7.6 关于MSR 一些后续说明 129 第二篇 处理器的工作模式 第8 章实地址模式 132 8.1 真实的地址 132 8.2 real mode 的编址 132 8.3 real mode 的状态 133 8.4 段基址的计算 134 8.5 第1 条执行的指令 134 8.6 实模式下的执行环境 135 8.7 实模式下的IVT 135 8.8 突破64K 段限 136 8.9 A20 地址线 137 第 9 章 SMM系统管理模式探索 138 9.1 进入SMM 138 9.2 SMM 的运行环境 141 9.2.1 SMRAM 区域 141 9.2.2 SMM 执行环境的初始化 143 9.2.3 SMM 下的operand 与address 144 9.2.4 SMM 下的CS 与EIP 144 9.2.5 SMM 下的SS 与ESP 145 9.3 SMM 里的中断 145 9.4 SMI 的Back-to-Back 响应 147 9.5 SMM 里开启保护模式 147 9.6 SMM 的版本 148 9.7 I/O 指令的重启及Halt 重启 151 9.8 SMM 的退出 152 9.9 SMBASE 的重定位. 153 9.10 SMI 处理程序的初始化 154 9.11 SMM 的安全 156 9.11.1 芯片组的控制 156 9.11.2 处理器对SMRAM 空间的限制 158 9.11.3 cache 的限制 160 9.12 测试SMI 处理程序 161 第 10 章 x86/x64 保护模式体系(上) 163 10.1 x86/x64 的权限 164 10.2 保护模式下的环境 164 10.2.1 段式管理所使用的资源 165 10.2.2 paging 分页机制所使用的资源 165 10.3 物理地址的产生 166 10.4 段式管理机制 167 10.4.1 段式内存管理 168 10.4.2 段式的保护措施 168 10.5 段式管理的数据结构 169 10.5.1 Segment Selector(段选择子) 169 10.5.2 Descriptor Table(描述符) 172 10.5.3 Segment Selector Register(段寄存器) 174 10.5.4 Segment Descriptor(段描述符) 175 10.5.5 LDT 描述符LDT 258 10.6 开启保护模式 259 10.6.1 初始化GDT 260 10.6.2 初始化IDT. 262 10.6.3 切换到保护模式 263 第11 章 x86/x64 保护模式体系(下) 265 11.1 物理页面 265 11.1.1 处理器的最高物理地址(MAXPHYADDR) 266 11.1.2 物理页面的大小 267 11.1.3 页转换模式(Paging Mode) 268 11.2 paging 机制下使用的资源 270 11.2.1 寄存器 270 11.2.2 CPUID 查询leaf 270 11.2.3 寄存器的控制位 271 11.2.4 页转换资源 272 11.3 32 位paging 模式(non-PAE 模式) 273 11.3.1 CR3 结构 274 11.3.2 32 位paging 模式下的PDE 结构 275 11.3.3 使用32 位paging 279 11.4 PAE paging 模式. 282 11.4.1 在Intel64 下的CR3 与PDPTE 寄存器 283 11.4.2 在AMD64 下的CR3 285 11.4.3 PAE paging 模式里的PDPTE 结构 286 11.4.4 PAE paging 模式里的PDE 结构 286 11.4.5 PAE paging 模式里的PTE 结构 288 11.4.6 使用和测试PAE paging 模式 288 11.4.7 使用和测试Execution Disable 功能 292 11.5 IA-32e pagi
首先,需要了解一下全局描述符(Global Descriptor Table,GDT)是什么。GDT是在操作系统启动时由CPU初始化的一个数据结构,用于管理内存段的访问权限。操作系统可以通过修改GDT来控制内存的访问权限和保护。 在Linux系统中,GDT的初始化是在setup.s文件中完成的。下面是setup.s文件中初始化GDT的代码和注释: ``` /* * 初始化GDT */ init_gdt: lgdt gdtr /* 加载GDT寄存器 */ /* GDT项0:空描述符 */ gdt_null: .word 0, 0 .byte 0, 0, 0, 0 /* GDT项1:内核代码段描述符 */ gdt_code: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x9a, 0xcf /* 基地址为0,代码段可读可执行,DPL为0,系统段,粒度为4KB */ /* GDT项2:内核数据段描述符 */ gdt_data: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x92, 0xcf /* 基地址为0,数据段可读可写,DPL为0,系统段,粒度为4KB */ /* GDT项3:用户代码段描述符 */ gdt_user_code: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x9a, 0xcf /* 基地址为0,代码段可读可执行,DPL为3,用户段,粒度为4KB */ /* GDT项4:用户数据段描述符 */ gdt_user_data: .word 0xffff, 0 /* 段界限 */ .byte 0, 0, 0x92, 0xcf /* 基地址为0,数据段可读可写,DPL为3,用户段,粒度为4KB */ /* GDT项5:TSS描述符 */ gdt_tss: .word 0x67, 0 /* 段界限 */ .byte 0, 0, 0x89, 0x40 /* 基地址为tss_entry,DPL为0,系统段,粒度为1B */ .word 0 /* tss_entry的低16位 */ .byte 0 /* tss_entry的第24位 */ .byte 0x00 /* tss_entry的第32位 */ .word 0 /* tss_entry的高16位 */ /* GDT项6:LDT描述符 */ gdt_ldt: .word 0, 0 /* 段界限 */ .byte 0, 0, 0x82, 0x40 /* 基地址为ldt,DPL为0,系统段,粒度为1B */ /* GDT项7:TSS段 */ gdt_tss_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT项8:LDT段 */ gdt_ldt_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT项9:TSS段 */ gdt_tss2_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT项10:LDT段 */ gdt_ldt2_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT项11:TSS段 */ gdt_tss3_entry: .word 0, 0 /* 填充 */ .word 0x10, 0 /* ss0 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ /* GDT项12:LDT段 */ gdt_ldt3_entry: .word 0, 0 /* 填充 */ .byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 填充 */ .fill 16 * 8 - (. - init_gdt) /* 填充到16字节 */ /* GDT */ gdt: .word 16 * 8 - 1 /* GDT长度 */ .long gdt /* GDT地址 */ /* GDT寄存器 */ gdtr: .word 16 * 8 - 1 /* GDT长度 */ .long gdt /* GDT地址 */ ``` 上述代码中,首先使用`lgdt`指令将GDT的长度和地址加载到GDT寄存器中。然后,定义了一些GDT项,包括空描述符、内核代码段描述符、内核数据段描述符、用户代码段描述符、用户数据段描述符、TSS描述符LDT描述符。其中,每个GDT项包括段界限、基地址、段属性等信息。 最后,定义了一个GDT数组`gdt`,将GDT长度和地址存储在`gdtr`寄存器中,完成了GDT的初始化。 需要注意的是,上述代码只是一个简化的示例,实际的GDT项可能更加复杂,具体的实现方式也可能因操作系统的不同而有所差异。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值