浏览代码

中断处理程序

simon 1 年之前
父节点
当前提交
5a54f04c5a
共有 5 个文件被更改,包括 87 次插入15 次删除
  1. 4 4
      build.sh
  2. 1 1
      clean.sh
  3. 51 2
      kernel/interrupt.c
  4. 8 2
      kernel/interrupt.h
  5. 23 6
      kernel/kernel.S

+ 4 - 4
build.sh

@@ -1,9 +1,9 @@
 #!/bin/sh
 
 echo "Compiling mbr..."
-nasm -I boot/include/ -o boot/mbr.bin boot/mbr.S
+nasm -I boot/include/ -o build/mbr.bin boot/mbr.S
 echo "Compiling loader..."
-nasm -I boot/include/ -o boot/loader.bin boot/loader.S
+nasm -I boot/include/ -o build/loader.bin boot/loader.S
 echo "Compiling kernel..."
 i386-elf-gcc -I ./lib/ -I lib/kernel/ -I kernel/ -c -fno-builtin -o build/main.o kernel/main.c
 nasm -f elf -o build/print.o lib/kernel/print.S
@@ -19,9 +19,9 @@ bximage -q -func=create -hd=30M hd30M.img
 
 echo "Installing to disk image..."
 echo " 0. Writing mbr to disk..."
-dd if=boot/mbr.bin of=hd30M.img bs=512 count=1 conv=notrunc
+dd if=build/mbr.bin of=hd30M.img bs=512 count=1 conv=notrunc
 echo " 1. Writing loader to disk..."
-dd if=boot/loader.bin of=hd30M.img bs=512 count=4 seek=2 conv=notrunc
+dd if=build/loader.bin of=hd30M.img bs=512 count=4 seek=2 conv=notrunc
 echo " 2. Writing kernel to disk..."
 dd if=build/kernel.bin of=hd30M.img bs=512 count=200 seek=9 conv=notrunc
 echo "Disk image created successfully."

+ 1 - 1
clean.sh

@@ -1,3 +1,3 @@
 #!/bin/sh
 
-rm -rf bochs_log.txt hd30M.img *.bin boot/*.bin build/*.o build/kernel.bin
+rm -rf bochs_log.txt hd30M.img *.bin build/*.o build/*.bin

+ 51 - 2
kernel/interrupt.c

@@ -4,6 +4,9 @@
 #include "../lib/kernel/print.h"
 #include "../lib/kernel/io.h"
 
+char *intr_name[IDT_DESC_CNT];        // 用于保存异常的名字
+intr_handler idt_table[IDT_DESC_CNT]; // 中断处理函数数组
+
 /*创建中断门描述符*/
 static void make_idt_desc(gate_desc *p_gdesc, uint8_t attr, intr_handler function)
 {
@@ -46,12 +49,58 @@ static void pic_init(void)
     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();
-    pic_init(); // 初始化可编程中断控制器8259A
+    itd_desc_init();  // 初始化中断描述符表
+    exception_init(); // 完成一般中断处理函数注册及异常名称注册
+    pic_init();       // 初始化可编程中断控制器8259A
 
     /* 加载 idt */
     uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)((uint32_t)idt << 16)));

+ 8 - 2
kernel/interrupt.h

@@ -30,7 +30,13 @@ static void make_idt_desc(gate_desc *p_gdesc, uint8_t attr, intr_handler functio
 static void itd_desc_init(void);
 void itd_init(void);
 
-static gate_desc idt[IDT_DESC_CNT];                 // 中断描述符表,本质上就是个中断门描述符数组
-extern intr_handler intr_entry_table[IDT_DESC_CNT]; // 声明引用定义在 kernel.S 中的中断处理函数入口数组
+static gate_desc idt[IDT_DESC_CNT]; // 中断描述符表,本质上就是个中断门描述符数组
+// 定义中断处理程序数组.在 kernel.S 中定义的intrXXentry只是中断处理程序的入口,最终调用的是ide_table中的处理程序
+extern intr_handler intr_entry_table[IDT_DESC_CNT];
+
+// 通用的中断处理函数,一般用在异常出现时的处理
+static void general_intr_handler(uint8_t vec_nr);
+// 完成一般中断处理函数注册及异常名称注册
+static void exception_init(void);
 
 #endif // __KERNEL_INTERRUPT_H

+ 23 - 6
kernel/kernel.S

@@ -6,6 +6,7 @@
 %define ZERO push 0
 
 extern put_str       ; 声明外部函数
+extern idt_table     ; idt_table 是 C 中注册的中断处理程序数组
 
 section .data
 intr_str db "interrup occur!", 0xa, 0
@@ -15,24 +16,40 @@ intr_entry_table:
 %macro VECTOR 2
 section .text
 intr%1entry:         ; 每个中断处理程序都要压入中断向量号,所以一个中断类型一个中断处理程序
-    %2
-    push intr_str
-    call put_str
-    add esp, 4       ; 清除参数
+    %2               
+    ; 保存上下文环境
+    push ds
+    push es 
+    push fs 
+    push gs
+    pushad      ; 保存通用寄存器的值, 依次是 eax, ecx, edx, ebx, esp, ebp, esi, edi
 
     ; 如果从片上进入的中断,除了从片上发送 EOI 外,还要往主片上发送 EOI
     mov al, 0x20    ; EOI 中断结束命令
     out 0xa0, al    ; 发送给从片
     out 0x20, al    ; 发送给主片
 
-    add esp, 4      ; 清除错误码, error_code
-    iret            ; 中断返回, 会自动从栈中弹出 eip, cs, eflags, esp, ss
+    push %1         ; 不管 idt_table 中的目标程序是否需要参数,都保存中断向量号,调试方便
+    call [idt_table + %1 * 4] ; 调用中断处理程序(中断处理程序的地址存储在 idt_table 中,是 C 中注册的中断处理程序数组)
+    jmp intr_exit   ; 中断处理程序执行完毕,跳转到中断退出程序
 
 section .data
     dd intr%1entry  ; 存储各个中断入口程序的地址,形成 intr_entry_table 数组
 
 %endmacro
 
+section .text
+global intr_exit
+intr_exit:
+    add esp, 4  ; 跳过中断向量号
+    popad       ; 恢复通用寄存器的值
+    pop gs
+    pop fs
+    pop es
+    pop ds
+    add esp, 4  ; 由于中断发生时,CPU 会自动压入错误码,所以在中断退出时,需要将错误码弹出
+    iretd       ; 从中断返回
+
 VECTOR 0x00, ZERO
 VECTOR 0x01, ZERO
 VECTOR 0x02, ZERO