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

来源:百度文库 编辑:神马文学网 时间:2024/04/28 14:43:11
内核--中断处理部分【2】——初始化IDT 

;;;;{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);
                }