三、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, 0x0, 0xFFFFFFFF, DPL_KERNEL), //kernel text
SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_KERNEL), //kernel data
SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_USER), //user text
SEG(STA_W, 0x0, 0xFFFFFFFF, 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) {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define SEG(type, base, lim, dpl) \
(struct segdesc) { \
((lim) >> 12) & 0xffff, (base) & 0xffff, \
((base) >> 16) & 0xff, type, 1, dpl, 1, \
(unsigned)(lim) >> 28, 0, 0, 1, 1, \
(unsigned) (base) >> 24 \
}
#define SEGTSS(type, base, lim, dpl) \
(struct segdesc) { \
(lim) & 0xffff, (base) & 0xffff, \
((base) >> 16) & 0xff, type, 0, dpl, 1, \
(unsigned) (lim) >> 16, 0, 0, 1, 0, \
(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(gate, istrap, sel, off, dpl) { \
(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