内核--中断处理部分【2】——初始化IDT

来源:百度文库 编辑:神马文学网 时间:2024/05/24 07:36:26
;;;;{2}中断描述符表IDT
;;;;[1]IDT与GDT是一样的,只是其中存放的是门描述符Gate Descriptor
加载IDT使用lidt [idt_ptr]
t_8 idt_ptr[6];        [0-1]是IDT界限,[2-5]是IDT基址
GATE idt[IDT_SIZE];
;;;;[2]GATE结构:
门描述符结构如下:
|-------------------------------------------------------------------------------|
|--Byte7--|--Byte6--|--Byte5--|--Byte4--|--Byte3--|--Byte2--|--Byte1--|--Byte0--|
|-------------------------------------------------------------------------------|
|     偏移地址      |     属性字节      |      选择子       |     偏移地址      |
|     [31-16]       |                   |                   |      [15-0]       |
|-------------------------------------------------------------------------------|
属性字节结构:
|-----------------------------------------------------------------------------------------------|
|--                   Byte5                   --|--                   Byte4                   --|
|-----------------------|-----------------------|-----------------|-----------------------------|
|--7--|--6--|--5--|--4--|--3--|--2--|--1--|--0--|--7--|--6--|--5--|--4--|--3--|--2--|--1--|--0--|
|-----------------------|-----------------------|-----------------|-----------------------------|
| G |    DPL    | S |         TYPE          | 0 | 0 | 0 |         Param Count         |
|-----------------------------------------------------------------------------------------------|
GATE的C语言定义:
typedef struct tag_GATE
{
t_16 offset_0_15;     /*偏移地址*/
t_16 selector;        /*选择子*/
t_8 param_count;     /*参数数目*/
t_8 attr;            /*属性字节,Byte5*/
t_16 offset_16_31;    /*偏移地址*/
}GATE;
;;;;[3]IDT描述符初始化
初始化主要工作:
1.定义IDT,把它和GDT放在一起,在protect.h中:
PUBLIC t_8 idt_ptr[6];        /*IDT指针,[0-1]是idt界限,[2-5]是idt基址*/
PUBLIC GATE idt[IDT_SIZE];    /*IDT空间*/
2.idt中存放的是中断和异常处理函数的入口地址, 先来准备这些入口地址:
先来处理异常,再分析中断.
先分析异常发生时的情形,CPU会将当前的cs,eip,eflags压入堆栈,
如果异常有错误码就将错误码也压入堆栈,然后转入idt中相应异常处理函数执行.
可以用一个函数来处理所有的异常,这个函数接收:异常的号码,异常的错误码(若没有错误码,
我们手工压入0xFFFFFFFF),当前cs,eip,eflags,定义如下的C函数:
exception_handler(vector, error_code, eip, cs, eflags)
在每个异常入口处,如果此异常没有错误码,就push 0xFFFFFFFF,然后用push语句将异常的号码vector压入堆栈,
最后jmp到调用exception_handler()函数的地方.
异常入口模版:
exception_name:
push error_code
push exception_vector
jmp LABEL_EXCEPTION_HANDLER
...
...
LABEL_EXCEPTION_HANDLER:
call exception_handler
add esp 2*4    ;vector和error_code出栈,保留cs,eip,eflags
这样写上近0个异常入口比较麻烦,写成一个宏来简化代码:
;异常入口宏
;;;;set_exception_handler(exception_name, has_error_code, vector)
%macro set_exception_handler 3
global %1
%1:    ;这里使用异常的名字作为标号,也就是异常处理的入口地址
%if %2 == FALSE
push 0xffffffff        ;无错误码
%endif
push %3                ;中断向量号
jmp LABEL_EXCEPTION_HANDLER    ;跳转到异常处理部分
%endmacro
异常入口代码如下:
;------- 下面是386支持的异常 ------
set_exception_handler div_error, FALSE, 0                ;除法错
set_exception_handler debug_exception, FALSE, 1          ;调试异常
set_exception_handler nmi, FALSE, 2                      ;非屏蔽外部中断
set_exception_handler break_point_exception, FALSE, 3    ;断点异常
set_exception_handler overflow, FALSE, 4                 ;溢出
set_exception_handler bounds_overrun, FALSE, 5           ;越界
set_exception_handler undefined_opcode, FALSE, 6         ;无效操作码
set_exception_handler no_math_copr, FALSE, 7             ;无数学协处理器
set_exception_handler double_fault, FALSE, 8             ;双重错误
set_exception_handler copr_seg_overrun, FALSE, 9         ;协处理器段越界
set_exception_handler invaild_tss, FALSE, 10             ;无效tss
set_exception_handler seg_not_present, FALSE, 11         ;段不存在
set_exception_handler stack_error, FALSE, 12             ;堆栈错误
set_exception_handler general_protection, FALSE, 13      ;常规保护错误
set_exception_handler page_fault, FALSE, 14              ;页错误
;;15号被intel保留,未使用
set_exception_handler copr_error, FALSE, 16              ;x87FPU错误
;;17,18,19号分别到486,奔腾,奔腾III才被支持
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
中断入口的构造方式与异常类似,只是中断没有错误码,发生中断时,cpu直接转入中断入口处.
同样写一个C函数来处理所有的中断:
irq_handler(int irq)
然后在中断入口处压入中断向量,并跳转到调用irp_handler()处.
中断入口模版:
push irq_vector
jmp LABEL_IRQ_HANDLER
...
...
LABEL_IRQ_HANDLER:
extern irq_handler
;;irq_handler(int irq)
call irq_handler    ;调用C语言中断处理函数
add esp, 1 * 4
同样写成一个宏:
;中断入口宏
;;;;set_irq_handler(irq_name, irq_num)
%macro set_irq_handler 2
global %1
%1:    ;这里使用中断的名字作为标号,也就是中断的入口
push %2        ;中断号入栈
jmp LABEL_IRQ_HANDLER    ;跳转到中断处理部分
%endmacro
中断入口代码如下:
;------- 下面是8259A的IRQ --------
set_irq_handler irq0, 0
set_irq_handler irq1, 1
set_irq_handler irq2, 2
set_irq_handler irq3, 3
set_irq_handler irq4, 4
set_irq_handler irq5, 5
set_irq_handler irq6, 6
set_irq_handler irq7, 7
set_irq_handler irq8, 8
set_irq_handler irq9, 9
set_irq_handler irq10, 10
set_irq_handler irq11, 11
set_irq_handler irq12, 12
set_irq_handler irq13, 13
set_irq_handler irq14, 14
set_irq_handler irq15, 15
set_irq_handler irq16, 16
3.中断和异常的入口都已经准备好,下面就要将这些入口地址填入idt中,以便发生中断和异常时
cpu能转入相应的代码运行.
这个就很容易了,无非就是填充很多个GATE结构,将所有的异常和中断处理入口地址,属性,参数个数.
写了一个函数来做这些,不过后来感觉还是宏速度快,函数倒是能省空间,不深究了....
/*初始化IDT描述符*/
PUBLIC void init_idt_desc(int vector, t_pf_int_handler handler, t_8 attr)
{
if (vector < 0 || vector > IDT_SIZE)
{
disp_str("\ninit_idt_desc() error -> vector not in [0-255]: vector=", 0x16);
disp_int(vector, 0x16);
return;
}
idt[vector].selector = SelectorPMCode;
idt[vector].offset_0_15 = (t_16)(((t_32)handler) & 0xffff);
idt[vector].offset_16_31 = (t_16)((((t_32)handler) >> 16) & 0xffff);
idt[vector].param_count = 0;
idt[vector].attr = attr;
}
然后再来个init_idt()函数:
PUBLIC void init_idt()
{
t_16 *p_idt_limit = (t_16 *)(&idt_ptr[0]);
t_32 *p_idt_base = (t_32 *)(&idt_ptr[2]);
*p_idt_limit = IDT_SIZE * sizeof(GATE) - 1;
*p_idt_base = (t_32)(&idt);
/* 初始化异常*/
init_idt_desc(0,div_error, DA_386_INT_GATE_DPL0);
init_idt_desc(1,debug_exception, DA_386_INT_GATE_DPL0);
init_idt_desc(2,nmi, DA_386_INT_GATE_DPL0);
init_idt_desc(3,break_point_exception, DA_386_INT_GATE_DPL3);    /*这里两个是用户级的,不是很明白为什么要这样做*/
init_idt_desc(4,overflow, DA_386_INT_GATE_DPL3);
init_idt_desc(5,bounds_overrun, DA_386_INT_GATE_DPL0);
init_idt_desc(6,undefined_opcode, DA_386_INT_GATE_DPL0);
init_idt_desc(7,no_math_copr, DA_386_INT_GATE_DPL0);
init_idt_desc(8,double_fault, DA_386_INT_GATE_DPL0);
init_idt_desc(9,copr_seg_overrun, DA_386_INT_GATE_DPL0);
init_idt_desc(10,invaild_tss, DA_386_INT_GATE_DPL0);
init_idt_desc(11,seg_not_present, DA_386_INT_GATE_DPL0);
init_idt_desc(12,stack_error, DA_386_INT_GATE_DPL0);
init_idt_desc(13,general_protection, DA_386_INT_GATE_DPL0);
init_idt_desc(14,page_fault, DA_386_INT_GATE_DPL0);
init_idt_desc(16,copr_error, DA_386_INT_GATE_DPL0);
/* 初始化中断*/
init_idt_desc(INT_VECTOR_8259_M_IRQ0 + 0, irq0, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_M_IRQ0 + 1, irq1, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_M_IRQ0 + 2, irq2, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_M_IRQ0 + 3, irq3, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_M_IRQ0 + 4, irq4, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_M_IRQ0 + 5, irq5, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_M_IRQ0 + 6, irq6, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_M_IRQ0 + 7, irq7, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_S_IRQ0 + 0, irq8, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_S_IRQ0 + 1, irq9, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_S_IRQ0 + 2, irq10, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_S_IRQ0 + 3, irq11, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_S_IRQ0 + 4, irq12, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_S_IRQ0 + 5, irq13, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_S_IRQ0 + 6, irq14, DA_386_INT_GATE_DPL0);
init_idt_desc(INT_VECTOR_8259_S_IRQ0 + 7, irq15, DA_386_INT_GATE_DPL0);
}