初步实现中断处理

1.kernel.S

①有些中断处理,会在栈中留下自己的错误码,就很像Linux的errno,所以当中断处理函数退栈的时候,会造成两种不同的处理方式,这里为了简便,直接将两种一起处理,有错误码的我们执行一条空指令,没有错误码的我们手工压入一个0
②我们在这里采用了宏汇编,来对所有的中断函数进行处理,就是当中断发生的时候,调用宏汇编的函数,在宏汇编的函数中,它压入自己的中断号,然后在IDT中查找自己的中断处理函数,进行处理

[bits 32]
%define ERROR_CODE nop		 
; 若在相关的异常中cpu已经自动压入了错误码,为保持栈中格式统一,这里不做操作.
%define ZERO push 0		 
; 若在相关的异常中cpu没有压入错误码,为了统一栈中格式,就手工压入一个0

extern put_str			 ;声明外部函数
extern g_IDT_Table
section .data
intr_str db "interrupt occur!", 0xa, 0
global g_IntrEntryTable
g_IntrEntryTable:

%macro VECTOR 2
section .text
intr%1entry:		 ; 每个中断处理程序都要压入中断向量号,所以一个中断类型一个中断处理程序,自己知道自己的中断向量号是多少
   %2

   ;保存上下文环境
	PUSH DS
	PUSH ES
	PUSH FS
	PUSH GS
	PUSHAD

   ; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI 
   mov al,0x20                   ; 中断结束命令EOI
   out 0xa0,al                   ; 向从片发送
   out 0x20,al                   ; 向主片发送

   PUSH %1 						 ;压入中断向量号
   CALL [g_IDT_Table+ %1*4]		 ;调用中断处理函数
	
   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					 ;跳过错误码
	IRETD



VECTOR 0x00,ZERO
VECTOR 0x01,ZERO
VECTOR 0x02,ZERO
VECTOR 0x03,ZERO 
VECTOR 0x04,ZERO
VECTOR 0x05,ZERO
VECTOR 0x06,ZERO
VECTOR 0x07,ZERO 
VECTOR 0x08,ERROR_CODE
VECTOR 0x09,ZERO
VECTOR 0x0a,ERROR_CODE
VECTOR 0x0b,ERROR_CODE 
VECTOR 0x0c,ZERO
VECTOR 0x0d,ERROR_CODE
VECTOR 0x0e,ERROR_CODE
VECTOR 0x0f,ZERO 
VECTOR 0x10,ZERO
VECTOR 0x11,ERROR_CODE
VECTOR 0x12,ZERO
VECTOR 0x13,ZERO 
VECTOR 0x14,ZERO
VECTOR 0x15,ZERO
VECTOR 0x16,ZERO
VECTOR 0x17,ZERO 
VECTOR 0x18,ERROR_CODE
VECTOR 0x19,ZERO
VECTOR 0x1a,ERROR_CODE
VECTOR 0x1b,ERROR_CODE 
VECTOR 0x1c,ZERO
VECTOR 0x1d,ERROR_CODE
VECTOR 0x1e,ERROR_CODE
VECTOR 0x1f,ZERO 
VECTOR 0x20,ZERO

2.Interrupt.c

①定义了中断描述符表
②定义了异常发生时,显示在屏幕上的错误信息
③定义了中断处理函数的数组
④声明了中断处理函数的入口,定义在kernel.S中
⑤对PIC进行了设定
⑥加载中段描述符表寄存器

#include "Interrupt.h"
#include "stdint.h"
#include "Global.h"
#include "print.h"
#include "io.h"


#define PIC_M_CTRL 0x20	       
// 这里用的可编程中断控制器是8259A,主片的控制端口是0x20
#define PIC_M_DATA 0x21	       // 主片的数据端口是0x21
#define PIC_S_CTRL 0xa0	       // 从片的控制端口是0xa0
#define PIC_S_DATA 0xa1	       // 从片的数据端口是0xa1
#define IDT_DESC_COUNT 0x21		//目前支持的中断总数

typedef struct INT_GATE_DESC_T			//中断门描述符的结构体定义
{
	uint16_t FunOffsetLowWord;	
	uint16_t Selector;
	uint8_t  dCount;
	uint8_t  Attribute;
	uint16_t FunOffsetHighWord;
}INT_GATE_DESC_T;



//用来保存异常的名字
char * IntrName[IDT_DESC_COUNT];

//定义中断处理程序数组
Int_Handler g_IDT_Table[IDT_DESC_COUNT];

//初始化一个中断描述符的结构体
static void MakeIdtDesc(INT_GATE_DESC_T *pGateDesc, const uint8_t attr, const Int_Handler pIntrFun);

//中断门描述符的数组
static INT_GATE_DESC_T g_IDT[IDT_DESC_COUNT];

//此数组定义在kernel.S文件
extern Int_Handler g_IntrEntryTable[IDT_DESC_COUNT];


//通用的中断处理函数 一般在异常出现时的处理
static void GeneralIntrHandler(uint8_t vec_num)
{
	//IRQ7 IRQ15 会产生伪中断 无需处理
	if(0x27 == vec_num || 0x2f == vec_num)
		return ;

	put_str("INT VECTOR : 0x");
	put_int(vec_num);
	put_char('\n');
}


static void InitException(void)
{
	int i;
	for(i = 0; i < IDT_DESC_COUNT; i++)
	{
		g_IDT_Table[i] = GeneralIntrHandler;
		IntrName[i] = "UNKNOWN";
	}

	IntrName[0] = "#DE Divide Error";
	IntrName[1] = "#DB Debug Exception";
	IntrName[2] = "NMI Interrupt";
	IntrName[3] = "#BP Breakpoint Exception";
	IntrName[4] = "#OF Overflow Exception";
	IntrName[5] = "#BR BOUND Range Exceeded Exception";
	IntrName[6] = "#UD Invalid Opcode Exception";
	IntrName[7] = "#NM Device Not Available Exception";
	IntrName[8] = "#DF Double Fault Exception";
	IntrName[9] = "Coprocessor Segment Overrun";
	IntrName[10] = "#TS Invalid TSS Exception";
	IntrName[11] = "#NP Segment Not Present";
	IntrName[12] = "#SS Stack Fault Exception";
	IntrName[13] = "#GP General Protection Exception";
	IntrName[14] = "#PF Page-Fault Exception";
	   // intr_name[15] 第15项是intel保留项,未使用
	IntrName[16] = "#MF x87 FPU Floating-Point Error";
	IntrName[17] = "#AC Alignment Check Exception";
	IntrName[18] = "#MC Machine-Check Exception";
	IntrName[19] = "#XF SIMD Floating-Point Exception";

}


/* 初始化可编程中断控制器8259A */
static void InitPic(void) 
{

   /* 初始化主片 */
   outb (PIC_M_CTRL, 0x11);   // ICW1: 边沿触发,级联8259, 需要ICW4.
   outb (PIC_M_DATA, 0x20);   
// ICW2: 起始中断向量号为0x20,也就是IR[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,也就是IR[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("  INIT PIC  DONE\n");
}


static void MakeIdtDesc(INT_GATE_DESC_T *pGateDesc, const uint8_t attr, const Int_Handler pIntrFun)
{
	pGateDesc->FunOffsetHighWord = ((uint32_t)pIntrFun & 0xFFFF0000)>>16;
	pGateDesc->Attribute = attr;
	pGateDesc->dCount = 0;
	pGateDesc->Selector = SELECTOR_K_CODE;
	pGateDesc->FunOffsetLowWord = (uint32_t)pIntrFun & 0x0000FFFF;
}

//初始化中断描述符表
static void InitIdtDesc(void)
{
	int i;
	for(i = 0; i < IDT_DESC_COUNT; i++)
	{
		MakeIdtDesc(&g_IDT[i], IDT_DESC_ATTR_DPL0, g_IntrEntryTable[i]);
	}
	put_str("IDT DESC INIT DONE\n");
}

//完成有关中断的所有初始化工作
void InitIdt(void)
{
	put_str("IDT ININT START\n");
	InitIdtDesc();//初始化中断描述符表
	InitException();//异常名初始化 并注册通用的中断处理函数
	InitPic();//初始化8259A
	

	//IDTR寄存器低16位是IDT界限 高32位是IDT的线性基地址
	uint64_t IdtOperand = ((sizeof(g_IDT)-1) | ((uint64_t) ((uint32_t)g_IDT << 16)));
	
	asm volatile("LIDT %0":: "m" (IdtOperand));

	put_str("IDT INIT DONE\n");
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值