Skip to content

Latest commit

 

History

History
152 lines (111 loc) · 5.98 KB

README.md

File metadata and controls

152 lines (111 loc) · 5.98 KB

章节六 GDT全局描述表

归功于GRUB,你的内核不再是 real mode(实模式),而是处于protected mode(保护模式),该模式允许我们使用微处理器的所有能力,比如 虚拟内存管理,分页,安全的多任务。

GDT 是什么?

GDT ("Global Descriptor Table" 全局描述表) 是一个用来定义不同内存区的数据结构,其包括:基地址,大小,访问特权(比如执行和写),这些区域被称为 "segments(段)"。

我们将使用GDT定义不同的内存段:

为了保留作者的原义,保留了该段原文

  • "code": kernel code, used to stored the executable binary code
  • "data": kernel data
  • "stack": kernel stack, used to stored the call stack during kernel execution
  • "ucode": user code, used to stored the executable binary code for user program
  • "udata": user program data
  • "ustack": user stack, used to stored the call stack during execution in userland

中文解释

  • "code": 内核代码,用来存储可执行二进制代码
  • "data": 内核数据
  • "stack": 内核栈,用来存储内核执行的调用栈
  • "ucode": 用户代码,用来存储用户可执行程序的二进制代码
  • "udata": 用户程序数据
  • "ustack": 用户栈,用来存储用户态执行的调用栈

如何加载GDT?

GRUB初始化一个GDT,但这个GDT不属于我们的内核。 GDT是使用 LGDT(加载全局描述符) 汇编指令 加载的。 LGDT需要一个GDT描述结构的位置。

全局描述符表寄存器GDTR

GDTR寄存器中用于存放全局描述符表GDT的32位的线性基地址和16位的表限长值。基地址指定GDT表中字节0在线性地址空间中的地址,表长度指明GDT表的字节长度值。指令LGDT和SGDT分别用于加载和保存GDTR寄存器的内容。在机器刚加电或处理器复位后,基地址被默认地设置为0,而长度值被设置成0xFFFF。在保护模式初始化过程中必须给GDTR加载一个新值。

全局描述符表寄存器(GDTR):

GDTR

C 结构体:

struct gdtr {
	u16 limite;
	u32 base;
} __attribute__ ((packed));

注意: 指令 __attribute__ ((packed)) 用来告诉gcc,该结构体应该在内存中紧凑排布。没有该指令,gcc将包含一些用来优化执行期间访问内存对齐的字节。

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

GDT table 是如下段描述符结构体组成的数组:

GDTR

C 结构体:

struct gdtdesc {
	u16 lim0_15;
	u16 base0_15;
	u8 base16_23;
	u8 acces;
	u8 lim16_19:4;
	u8 other:4;
	u8 base24_31;
} __attribute__ ((packed));

如何定义我们的GDT table?

我们应该在内存中定义我们的GDT,然后使用 LGDT指令加载到GDTR寄存器

我们将GDT存储在内存如下地址:

#define GDTBASE	0x00000800

x86.cc 中的函数 init_gdt_desc 初始化一个GDT段描述符:

void init_gdt_desc(u32 base, u32 limite, u8 acces, u8 other, struct gdtdesc *desc)
{
	desc->lim0_15 = (limite & 0xffff);
	desc->base0_15 = (base & 0xffff);
	desc->base16_23 = (base & 0xff0000) >> 16;
	desc->acces = acces;
	desc->lim16_19 = (limite & 0xf0000) >> 16;
	desc->other = (other & 0xf);
	desc->base24_31 = (base & 0xff000000) >> 24;
	return;
}

函数 init_gdt 初始化GDT,下面涉及的其它函数后面将做解释,主要是用来多任务。

void init_gdt(void)
{
	default_tss.debug_flag = 0x00;
	default_tss.io_map = 0x00;
	default_tss.esp0 = 0x1FFF0;
	default_tss.ss0 = 0x18;

	/* initialize gdt segments */
	init_gdt_desc(0x0, 0x0, 0x0, 0x0, &kgdt[0]);
	init_gdt_desc(0x0, 0xFFFFF, 0x9B, 0x0D, &kgdt[1]);	/* code */
	init_gdt_desc(0x0, 0xFFFFF, 0x93, 0x0D, &kgdt[2]);	/* data */
	init_gdt_desc(0x0, 0x0, 0x97, 0x0D, &kgdt[3]);		/* stack */

	init_gdt_desc(0x0, 0xFFFFF, 0xFF, 0x0D, &kgdt[4]);	/* ucode */
	init_gdt_desc(0x0, 0xFFFFF, 0xF3, 0x0D, &kgdt[5]);	/* udata */
	init_gdt_desc(0x0, 0x0, 0xF7, 0x0D, &kgdt[6]);		/* ustack */

	init_gdt_desc((u32) & default_tss, 0x67, 0xE9, 0x00, &kgdt[7]);	/* descripteur de tss */

	/* 初始化全局描述符表寄存器gdtr结构体 */
	kgdtr.limite = GDTSIZE * 8;
	kgdtr.base = GDTBASE;

	/* 将kgdt数组 拷贝到了 起始地址为 GDTBASE 的内存区 */
	memcpy((char *) kgdtr.base, (char *) kgdt, kgdtr.limite);

	/* 加载到GDTR寄存器 */
	asm("lgdtl (kgdtr)");

	/* initiliaz the segments */
	asm("   movw $0x10, %ax	\n \
            movw %ax, %ds	\n \
            movw %ax, %es	\n \
            movw %ax, %fs	\n \
            movw %ax, %gs	\n \
            ljmp $0x08, $next	\n \
            next:		\n");
}

译者注:

下一章: IDT(中断描述符表)和中断