三、GDT和IDT的配置

在bootsector.S中我们就打开了CR0寄存器的第0位(设置为1),即打开了保护模式。接下来进入今天的主题GDT(全局描述符表)的设置。首先来看下intel手册中是如何规定gdt的。

intel内置48位的GDTR寄存器,低16位代表描述符的数量,最大可有8192个描述符,基地址则规定了GDT表的地址。那么GDT表中的段描述符长啥样呢?可以从下图中窥见一斑。

那么根据这个段描述符的描述,总共8个字节,我们可以设定一个结构体来描述一个段描述符。本文主要借鉴ucore的GDT设置方法,如下所示:

 

 

gdt.h

 

 

gdt.c

到这里的时候其实已经完成gdt表的设置,但为了看到段描述符的变化,我们写一个打印函数来证明cs、ds等确实发生了变化。

到这里,GDT的设置就基本完成了,接下来进行IDT的配置。首先来看IDT表的结构,和GDT表的结构十分相似。

出现以下错误:

主要原因是.S文件中使用了.h中的一个变量,所以加了其头文件,但在编译的时候无法按.S格式解析.h文件中的结构体等,所以报错,改正如下

 

人为输入int 0x1中断,可以看到系统调用了1号中断服务程序,运行成功。至此,GDT和IDT的配置基本完成,代码如下:

gdt.c

#include "gdt.h"

/* *

* Global Descriptor Table:

*

* The kernel and user segments are identical (except for the DPL). To load

* the %ss register, the CPL must equal the DPL. Thus, we must duplicate the

* segments for the user and the kernel. Defined as follows:

* - 0x0 : unused (always faults -- for trapping NULL far pointers)

* - 0x8 : kernel code segment

* - 0x10: kernel data segment

* - 0x18: user code segment

* - 0x20: user data segment

* - 0x28: defined for tss, initialized in gdt_init

* */

static struct segdesc gdt[] = {

SEG_NULL, //null

SEG(STA_X | STA_R, 0x00xFFFFFFFF, DPL_KERNEL), //kernel text

SEG(STA_W, 0x00xFFFFFFFF, DPL_KERNEL), //kernel data

SEG(STA_X | STA_R, 0x00xFFFFFFFF, DPL_USER), //user text

SEG(STA_W, 0x00xFFFFFFFF, DPL_USER), //user data

SEG_NULL, //tss

};

 

/* *

* Interrupt descriptor table:

*

* Must be built at run time because shifted function addresses can't

* be represented in relocation records.

* */

static struct gatedesc idt[256] = {{0}};

 

//set gdt's info

static struct dtdesc gdtinfo={sizeof(gdt)-1,(unsigned int)gdt};

 

//set idt's info

static struct dtdesc idtinfo = {sizeof(idt) - 1, (unsigned int)idt};

/* *

* lgdt - load the global descriptor table register and reset the

* data/code segement registers for kernel.

* */

static inline void lgdt(struct dtdesc *dt

{

asm volatile ("lgdt (%0)" :: "r" (dt));

asm volatile ("movw %%ax, %%gs" :: "a" (USER_DS));

asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS));

asm volatile ("movw %%ax, %%es" :: "a" (KERNEL_DS));

asm volatile ("movw %%ax, %%ds" :: "a" (KERNEL_DS));

asm volatile ("movw %%ax, %%ss" :: "a" (KERNEL_DS));

// reload cs

asm volatile ("ljmp %0, $1f\n 1:\n" :: "i" (KERNEL_CS));

}

static inline void lidt(struct dtdesc *dt

{

asm volatile ("lidt (%0)" :: "r" (dt) : "memory");

}

void gdt_init()

{

//load gdtinfo

lgdt(&gdtinfo);

}

void idt_init()

{

extern unsigned int __vectors[];

int i;

for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) {

SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);

}

SETGATE(idt[T_SYSCALL], 1, GD_KTEXT, __vectors[T_SYSCALL], DPL_USER);

lidt(&idtinfo);

}

 

gdt.h

 

/* Application segment type bits */

#define STA_X 0x8 // Executable segment

#define STA_E 0x4 // Expand down (non-executable segments)

#define STA_C 0x4 // Conforming code segment (executable only)

#define STA_W 0x2 // Writeable (non-executable segments)

#define STA_R 0x2 // Readable (executable segments)

#define STA_A 0x1 // Accessed

/* global descrptor numbers */

#define GD_KTEXT ((1) << 3) // kernel text

#define GD_KDATA ((2) << 3) // kernel data

#define GD_UTEXT ((3) << 3) // user text

#define GD_UDATA ((4) << 3) // user data

#define GD_TSS ((5) << 3) // task segment selector

#define DPL_KERNEL (0)

#define DPL_USER (3)

//global descriptor selector

#define KERNEL_CS (GD_KTEXT|DPL_KERNEL)

#define KERNEL_DS (GD_KDATA|DPL_KERNEL)

#define USER_CS (GD_UTEXT|DPL_USER)

#define USER_DS (GD_UDATA|DPL_USER)

/* System segment type bits */

#define STS_CG32 0xC // 32-bit Call Gate

#define STS_IG32 0xE // 32-bit Interrupt Gate

#define STS_TG32 0xF // 32-bit Trap Gate

#define SEG_NULL \

(struct segdesc) {0000000000000}

 

#define SEG(typebaselimdpl\

(struct segdesc) { \

((lim) >> 12& 0xffff, (base) & 0xffff\

((base) >> 16& 0xff, type, 1, dpl, 1\

(unsigned)(lim) >> 280011\

(unsigned) (base) >> 24 \

}

#define SEGTSS(typebaselimdpl\

(struct segdesc) { \

(lim) & 0xffff, (base) & 0xffff\

((base) >> 16& 0xff, type, 0, dpl, 1\

(unsigned) (lim) >> 160010\

(unsigned) (base) >> 24 \

}

#define T_SYSCALL 0x80

/* *

* Set up a normal interrupt/trap gate descriptor

* - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate

* - sel: Code segment selector for interrupt/trap handler

* - off: Offset in code segment for interrupt/trap handler

* - dpl: Descriptor Privilege Level - the privilege level required

* for software to invoke this interrupt/trap gate explicitly

* using an int instruction.

* */

#define SETGATE(gateistrapseloffdpl) { \

(gate).gd_off_15_0 = (unsigned int)(off) & 0xffff\

(gate).gd_ss = (sel); \

(gate).gd_args = 0\

(gate).gd_rsv1 = 0\

(gate).gd_type = (istrap) ? STS_TG32 : STS_IG32; \

(gate).gd_s = 0\

(gate).gd_dpl = (dpl); \

(gate).gd_p = 1\

(gate).gd_off_31_16 = (unsigned int)(off) >> 16\

}

/* segment descriptors */

//bit field

struct segdesc{

unsigned sd_lim_15_0 : 16; // low bits of segment limit

unsigned sd_base_15_0 : 16; // low bits of segment base address

unsigned sd_base_23_16 : 8; // middle bits of segment base address

unsigned sd_type : 4; // segment type (see STS_ constants)

unsigned sd_s : 1; // 0 = system, 1 = application

unsigned sd_dpl : 2; // descriptor Privilege Level

unsigned sd_p : 1; // present

unsigned sd_lim_19_16 : 4; // high bits of segment limit

unsigned sd_avl : 1; // unused (available for software use)

unsigned sd_rsv1 : 1; // reserved

unsigned sd_db : 1; // 0 = 16-bit segment, 1 = 32-bit segment

unsigned sd_g : 1; // granularity: limit scaled by 4K when set

unsigned sd_base_31_24 : 8; // high bits of segment base address

};

/* Gate descriptors for interrupts and traps */

struct gatedesc {

unsigned gd_off_15_0 : 16; // low 16 bits of offset in segment

unsigned gd_ss : 16; // segment selector

unsigned gd_args : 5; // # args, 0 for interrupt/trap gates

unsigned gd_rsv1 : 3; // reserved(should be zero I guess)

unsigned gd_type : 4; // type(STS_{TG,IG32,TG32})

unsigned gd_s : 1; // must be 0 (system)

unsigned gd_dpl : 2; // descriptor(meaning new) privilege level

unsigned gd_p : 1; // Present

unsigned gd_off_31_16 : 16; // high bits of offset in segment

};

 

//describe the gdt/ldt/idt,remember struct's member is first member in low address 

struct dtdesc{

unsigned short dt_size;

unsigned int dt_base;

}__attribute__((packed));

static inline void lgdt(struct dtdesc *dt);

static inline void lidt(struct dtdesc *dt);

void lidt(struct dtdesc *dt);

void gdt_init();

void idt_init();

trap.h

 

/* Trap Numbers */

 

/* Processor-defined: */

#define T_DIVIDE 0 // divide error

#define T_DEBUG 1 // debug exception

#define T_NMI 2 // non-maskable interrupt

#define T_BRKPT 3 // breakpoint

#define T_OFLOW 4 // overflow

#define T_BOUND 5 // bounds check

#define T_ILLOP 6 // illegal opcode

#define T_DEVICE 7 // device not available

#define T_DBLFLT 8 // double fault

// #define T_COPROC 9 // reserved (not used since 486)

#define T_TSS 10 // invalid task switch segment

#define T_SEGNP 11 // segment not present

#define T_STACK 12 // stack exception

#define T_GPFLT 13 // general protection fault

#define T_PGFLT 14 // page fault

// #define T_RES 15 // reserved

#define T_FPERR 16 // floating point error

#define T_ALIGN 17 // aligment check

#define T_MCHK 18 // machine check

#define T_SIMDERR 19 // SIMD floating point error

 

/* Hardware IRQ numbers. We receive these as (IRQ_OFFSET + IRQ_xx) */

#define IRQ_OFFSET 32 // IRQ 0 corresponds to int IRQ_OFFSET

 

#define IRQ_TIMER 0

#define IRQ_KBD 1

#define IRQ_COM1 4

#define IRQ_IDE1 14

#define IRQ_IDE2 15

#define IRQ_ERROR 19

#define IRQ_SPURIOUS 31

 

/* *

* These are arbitrarily chosen, but with care not to overlap

* processor defined exceptions or interrupt vectors.

* */

#define T_SWITCH_TOU 120 // user/kernel switch

#define T_SWITCH_TOK 121 // user/kernel switch

 

/* registers as pushed by pushal */

struct pushregs {

unsigned int reg_edi;

unsigned int reg_esi;

unsigned int reg_ebp;

unsigned int reg_oesp; /* Useless */

unsigned int reg_ebx;

unsigned int reg_edx;

unsigned int reg_ecx;

unsigned int reg_eax;

};

 

struct trapframe {

struct pushregs tf_regs;

unsigned short tf_gs;

unsigned short tf_padding0;

unsigned short tf_fs;

unsigned short tf_padding1;

unsigned short tf_es;

unsigned short tf_padding2;

unsigned short tf_ds;

unsigned short tf_padding3;

unsigned int tf_trapno;

/* below here defined by x86 hardware */

unsigned int tf_err;

unsigned int tf_eip;

unsigned short tf_cs;

unsigned short tf_padding4;

unsigned int tf_eflags;

/* below here only when crossing rings, such as from user to kernel */

unsigned int tf_esp;

unsigned short tf_ss;

unsigned short tf_padding5;

} __attribute__((packed));

static void trap_dispatch(struct trapframe *tf);

void trap(struct trapframe *tf);

 

 

trap.h

#include "trap.h"

#include "vga.h"

/* *

* trap - handles or dispatches an exception/interrupt. if and when trap() returns,

* the code in kern/trap/trapentry.S restores the old CPU state saved in the

* trapframe and then uses the iret instruction to return from the exception.

* */

/* trap_dispatch - dispatch based on what type of trap occurred */

static void trap_dispatch(struct trapframe *tf

{

char c;

 

switch (tf->tf_trapno) {

case 1:

print_string("interrupt 1!\n",black,green);

break;

case IRQ_OFFSET + IRQ_TIMER:

break;

case IRQ_OFFSET + IRQ_COM1:

break;

case IRQ_OFFSET + IRQ_KBD:

break;

case T_SWITCH_TOU:

break;

case T_SWITCH_TOK:

break;

case IRQ_OFFSET + IRQ_IDE1:

case IRQ_OFFSET + IRQ_IDE2:

/* do nothing */

break;

default:

// in kernel, it must be a mistake

print_string("unexpected trap in kernel!\n",black,green);

}

}

void trap(struct trapframe *tf

{

// dispatch based on what type of trap occurred

trap_dispatch(tf);

}

 

运行结果:

参考资料:

1.ucore操作系统

2.http://wiki.0xffffff.org/posts/hurlex-7.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GDT(全局描述符表)和IDT(中断描述符表)是操作系统中用于管理内存和中断的重要数据结构。 GDT是一个表,用于存储所有内存段的描述符,描述符包含了段的基地址、段的长度、访问权限等信息。在x86体系结构的保护模式下,所有的内存访问都必须通过段寄存器来实现。当CPU执行一条访存指令时,它会把段寄存器的值当做段描述符的索引,在GDT中找到对应的描述符,从而确定要访问的内存地址的范围和访问权限。因此,初始化GDT是操作系统启动时的必要步骤。 IDT是另一个表,用于存储所有中断和异常处理程序的描述符。当CPU收到一个中断请求或异常时,它会从IDT中找到对应的描述符,从而确定要执行的中断或异常处理程序的地址。因此,初始化IDT也是操作系统启动时的必要步骤。 GDTIDT的初始化大致可以分为以下几个步骤: 1.创建并填充GDTIDT的表项,每个表项对应一个内存段或中断处理程序。 2.创建并填充GDTR(GDT寄存器)和IDTR(IDT寄存器),这两个寄存器分别存储GDTIDT表的地址和大小信息。 3.使用LGDT和LIDT指令将GDTR和IDTR的值加载到CPU中,从而告诉CPU如何寻找GDTIDT表。 需要注意的是,为了保证安全性,GDTIDT表通常被放置在内核态的固定位置,并且只有内核态的代码才能够修改它们。此外,为了简化实现,现代操作系统通常会使用一些预定义的GDTIDT表项,而不是每次都手动填充表项。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值