最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

5.9 GDT与IDT的初始化(harib02i)

来源:博客园

5.9 GDT与IDT的初始化(harib02i)

CPU用8字节来表示一个段的1.段的大小2.段的起始地址3.段的管理属性(禁止写入,禁止执行,系统专用)信息,但8字节(64位)远大于段寄存器(16位),故用段寄存器来存储段号,由于16位中的低三位不能用,故只有0~8191的区域,即8192个段8192*8=68836字节(64kb),64kb的数据CPU无法储存,故存储在内存中,这64kb的数据即为GDT(global(segment) descriptor table)全局段号记录表,而为了使用GDT方便,便将GDT的首地址有效设定个数存入GDTR(global(segment) descriptor table register)特殊寄存器中

  • GDT:global(segment) descriptor table全局段号记录表
  • 段寄存器:16位

  • IDT:interrupt descriptor table 中断记录表IDT记录了0~255的中断号码与调用函数的对应关系,其设定方法与GDT相似(也许是因为使用同样方法可以简化CPU电路


    (资料图片)

注意:设定IDT(interrupt descriptor table)最好先于设定GDT(global(segment) descriptor table),否则会比较麻烦

/*SEGMENT_DESCRIPTOR与GATE_DESCRIPTOR一样,均是以CPU的资料为基础,写成结构体的形式*/struct SEGMENT_DESCRIPTOR {//8字节=2*2字节+2*1字节+2*1字节short limit_low, base_low;//short类型占两字节char base_mid, access_right;char limit_high, base_high;};struct GATE_DESCRIPTOR {//8字节=2*2字节+2*1字节+1*2字节short offset_low, selector;//short类型占两字节char dw_count, access_right;short offset_high;};
void init_gdtidt(void){    /*表明要使用内存0x00270000~0x0027ffff的65535B设为GDT,原因是这一块内存未被使用*/struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000;    /*表明要使用0x0026f800~0x00270000的2048B设为IDT,这也应证了“有256个中断号码,用8B表示每个中断对应的信息”*/struct GATE_DESCRIPTOR    *idt = (struct GATE_DESCRIPTOR    *) 0x0026f800;int i;/* global(segment) descriptor table  全局短号记录表*//* GDT初始化 */for (i = 0; i < 8192; i++) {/*此处要注意C语言进行指针的加法运算时,内部隐含着乘法运算*/set_segmdesc(gdt + i, 0, 0, 0);}    /*设定,段号为1的段,上限值为0xffffffff(即大小正好是4GB),地址是0,它表示的是CPU所能管理的全部内存本身。段的属性设为0x4092,它的含义我们留待明天再说*/set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092);    /*段号为2的段,它的大小是512KB,地址是0x280000。这正好是为bootpack.hrb而准备的。这个段可以执行bootpack.hrb。因为bootpack.hrb是以ORG 0为前提翻译成的机器语言。*/set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a);load_gdtr(0xffff, 0x00270000);/*同下方的load_idtr(0x7ff, 0x0026f800);,C语言不能给GDTR赋值*//* interrupt descriptor table  中断记录表*//* IDT初始化 */for (i = 0; i < 256; i++) {set_gatedesc(idt + i, 0, 0, 0);}load_idtr(0x7ff, 0x0026f800);return;}
//上限(limit,指段的字节数-1),基址(base),访问权限void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar){if (limit > 0xfffff) {ar |= 0x8000; /* G_bit = 1 */limit /= 0x1000;}sd->limit_low    = limit & 0xffff;sd->base_low     = base & 0xffff;sd->base_mid     = (base >> 16) & 0xff;sd->access_right = ar & 0xff;sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);sd->base_high    = (base >> 24) & 0xff;return;}
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar){gd->offset_low   = offset & 0xffff;gd->selector     = selector;gd->dw_count     = (ar >> 8) & 0xff;gd->access_right = ar & 0xff;gd->offset_high  = (offset >> 16) & 0xffff;return;}

此处将6.4中的内容提前到此处

_load_gdtr:; void load_gdtr(int limit, int addr);MOVAX,[ESP+4]; limitMOV[ESP+6],AXLGDT[ESP+6];将从地址[ESP+6]起的6字节数据赋值给GDTR寄存器RET

_load_gdtr函数的功能是将段上限(limit)地址值赋值给名为GDTR(global(segment) descriptor table register)48 位寄存器。注意:GDTR不能用正常的MOV指令进行赋值,只能使用LGDT指令

关于GDTR寄存器,其低16位(即内存的最初2个字节)是段上限(即GDT 的有效字节数 - 1),剩下的高32位, 代表GDT的开始地址

经过上图的变换,即可完成写入GDT的任务。

注意:从ESP到ESP+3的四字节空间应该是被函数_load_gdtr的什么属性占据了,查明资料后补充

struct SEGMENT_DESCRIPTOR {//8字节=2*2字节+2*1字节+2*1字节short limit_low, base_low;//short类型占两字节char base_mid, access_right;char limit_high, base_high;};

结构体中base分为low(2字节),mid(1字节),high(1字节) 3段,合起来刚好是32位。分三段是为了与80286时代的程序兼容,这样80286用的操作系统,可以不用修改就在386以后的CPU上运行了。

//上限(limit,指段的字节数-1),基址(base),访问权限void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar){if (limit > 0xfffff) {ar |= 0x8000; /* G_bit = 1 */limit /= 0x1000;}sd->limit_low    = limit & 0xffff;sd->base_low     = base & 0xffff;sd->base_mid     = (base >> 16) & 0xff;sd->access_right = ar & 0xff;sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);sd->base_high    = (base >> 24) & 0xff;return;}

也正因为是三段,所以以上set_segmdesc函数中需要使用移位运算符和AND运算符来填入数据

由于段上限最大是4GB,即一个32位(4字节)数值,所以段大小占4字节,基址(base 起始地址)再占4字节,则段的管理属性没地方保存。若想保存段的管理属性,则段的大小只能使用20位,则可以使用的段上限缩小为1MB。故因特尔的解决办法是给段增加一个属性——Gbit,该标志位为1时,将limit的单位解释为页(4KB),1M*4KB=4GB,则不会产生浪费。因此20位的段上限写到limit_low(2字节)limit_high(1字节)的低四位中,limit_high(1字节)的高四位写入段属性(还有一部分在access_right)。

access_right或ar的高4位(即limit_high(1字节)的高四位)被称为“扩展访问权”,因为它在80286时代不存在386后才可以用。这四位由“GD00”构成,G为上文中的Gbit标志位,D为段的模式(1是指32位模式,0是指16位模式),这里的16位模式主要只用于运行 80286的程序不能用于调用BIOS。所以,除了运行80286程序以外, 通常都使用D=1的模式。

access_right或ar的低4位在80286时代就已经有了,下面简单介绍以下

00000000(0x00):未使用的记录表(descriptor table)。10010010(0x92):系统专用,可读写的段。不可执行。10011010(0x9a):系统专用,可执行的段。可读不可写。11110010(0xf2):应用程序用,可读写的段。不可执行。11111010(0xfa):应用程序用,可执行的段。可读不可写。

32位模式下,CPU有系统模式(也称为“ring0”)应用模式(也称为“ring3”)

举例:在应用模式下不允许执行LGDT指令(从指定的地址读取6个字节(48位),然后赋值给GDTR),一旦可以执行LGDT便可以修改GDTR,从而对GDT进行修改

struct SEGMENT_DESCRIPTOR {//8字节=2*2字节+2*1字节+2*1字节short limit_low, base_low;//short类型占两字节char base_mid, access_right;char limit_high, base_high;};

关键词: