#include "interrupt.h" #include "stdint.h" #include "global.h" #include "../lib/kernel/print.h" #include "../lib/kernel/io.h" char *intr_name[IDT_DESC_CNT]; // 用于保存异常的名字 intr_handler idt_table[IDT_DESC_CNT]; // 中断处理函数数组 #define EFLAGS_IF 0x200 // eflags寄存器中的if位为1 #define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0" : "=g"(EFLAG_VAR)) static gate_desc idt[IDT_DESC_CNT]; // 中断描述符表,本质上就是个中断门描述符数组 /*创建中断门描述符*/ static void make_idt_desc(gate_desc *p_gdesc, uint8_t attr, intr_handler function) { p_gdesc->offset_low = (uint32_t)function & 0x0000FFFF; p_gdesc->selector = SELECTOR_K_CODE; p_gdesc->dcount = 0; p_gdesc->attribute = attr; p_gdesc->offset_high = ((uint32_t)function & 0xFFFF0000) >> 16; } /* 初始化中断描述符表*/ static void itd_desc_init(void) { int i; for (i = 0; i < IDT_DESC_CNT; i++) { make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); } put_str(" idt_desc_init done\n"); } /* 初始化可编程中断控制器 8259A */ static void pic_init(void) { /* 初始化主片*/ outb(PIC_M_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要 ICW4 outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号 0x20, 也就是 IRQ[0~7] 为 0x20~0x27 outb(PIC_M_DATA, 0x04); // ICW3: IR2 接从片 outb(PIC_M_DATA, 0x01); // ICW4: 8086 模式, 正常 EOI /* 初始化从片 */ outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要 ICW4 outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号 0x28, 也就是 IRQ[8~15] 以 0x28~0x2F outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的 IR2 引脚 outb(PIC_S_DATA, 0x01); // ICW4: 8086 模式, 正常 EOI /* 打开主片上 IR0, 也就是目前只接受时钟产生的中断*/ outb(PIC_M_DATA, 0xfe); outb(PIC_S_DATA, 0xff); put_str(" pic_init done\n"); } static void general_intr_handler(uint8_t vec_nr) { if (vec_nr == 0x27 || vec_nr == 0x2f) { // IRQ7 和 IRQ15 会产生伪中断,无需处理 // 0x2f 是从片8259A上的最后一个 IRQ 引脚,保留项 return; } put_str("int vector: 0x"); put_int(vec_nr); put_char('\n'); } static void exception_init(void) { int i; for (i = 0; i < IDT_DESC_CNT; i++) { /*idt_table 数组中的函数是进入中断后根据中断向量号调用的,见 kernel/kernel.S 的 call [itd_table + %1*4]*/ idt_table[i] = general_intr_handler; // 默认为 general_intr_handler intr_name[i] = "unknown"; } intr_name[0] = "#DE Divide Error"; intr_name[1] = "#DB Debug Exception"; intr_name[2] = "NMI Interrupt"; intr_name[3] = "#BP Breakpoint Exception"; intr_name[4] = "#OF Overflow Exception"; intr_name[5] = "#BR BOUND Range Exceeded Exception"; intr_name[6] = "#UD Invalid Opcode Exception"; intr_name[7] = "#NM Device Not Available Exception"; intr_name[8] = "#DF Double Fault Exception"; intr_name[9] = "Coprocessor Segment Overrun"; intr_name[10] = "#TS Invalid TSS Exception"; intr_name[11] = "#NP Segment Not Present"; intr_name[12] = "#SS Stack Fault Exception"; intr_name[13] = "#GP General Protection Exception"; intr_name[14] = "#PF Page-Fault Exception"; // intr_name[15] 第 15 项是 intel 保留项,未使用 intr_name[16] = "#MF x87 FPU Floating-Point Error"; intr_name[17] = "#AC Alignment Check Exception"; intr_name[18] = "#MC Machine-Check Exception"; intr_name[19] = "#XF SIMD Floating-Point Exception"; put_str(" exception_init done\n"); } /* 中断初始化主函数 */ void itd_init(void) { put_str("init_idt start\n"); itd_desc_init(); // 初始化中断描述符表 exception_init(); // 完成一般中断处理函数注册及异常名称注册 pic_init(); // 初始化可编程中断控制器8259A /* 加载 idt */ uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)((uint32_t)idt << 16))); asm volatile("lidt %0" ::"m"(idt_operand)); put_str("itd_init done\n"); } enum intr_status intr_get_status(void) { uint32_t eflags = 0; GET_EFLAGS(eflags); return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF; } enum intr_status intr_set_status(enum intr_status status) { return status & INTR_ON ? intr_enable() : intr_disable(); } enum intr_status intr_enable(void) { enum intr_status old_status; if (INTR_ON == intr_get_status()) { old_status = INTR_ON; return old_status; } else { old_status = INTR_OFF; asm volatile("sti"); // 开中断, sti 指令将IF位置1 return old_status; } } enum intr_status intr_disable(void) { enum intr_status old_status; if (INTR_ON == intr_get_status()) { old_status = INTR_ON; asm volatile("cli" : : : "memory"); // 关中断, cli 指令将IF位置0 return old_status; } else { old_status = INTR_OFF; return old_status; } }