1.1 中断的基本认识
1.1.a Intel x86共支持256种向量中断,可分为两大类:异常和中断。
异常又分为故障(Fault)和陷阱(Trap),它们的共同特点是既不使用中断控制器,又不能被屏蔽。
中断又分为外部可屏蔽中断(INTR)和外部非屏蔽中断(NMI),所有I/O设备产生的中断请求(IRQ)均引起屏蔽中断,而紧急的事件(如硬件故障)引起的故障产生非屏蔽中断。
1.1.b Linux对256个向量的分配如下:
0~31对应于异常和非屏蔽中断。
32~47(即由I/O设备引起的中断)分配给屏蔽中断。
48~255的向量用来标识软中断。Linux只用了其中的一个(即128或0x80向量)用来实现系统调用。
1.2 代码
1.2.1 不用《操作系统真相还原》代码的原因
没有看懂 第7章中断代码里面, intr_entry_table 是怎么样与 宏里面的 intr%1entry 对应起来的?
这个地方实在理解不了,为啥放在section .data里面就可以自动初始化了?看起来就有点悬。
-
a.
-
intr0x00entry
-
intr0x01entry
-
...
-
intr0x20entry
-
怎么就会初始化了 数组 intr_entry_table[IDT_DESC_CNT]?
-
-
b.
-
要初始化数组intr_entry_table,得有
-
intr_entry_table[0] = intr0x00entry ;
-
intr_entry_table[1] = intr0x01entry ;
-
...
-
intr_entry_table[0x20] = intr0x20entry ;
-
-
才行,但是没有看到
-
-
c.
-
从debug的结果上肯定是,
-
intr0x00entry
-
intr0x01entry
-
...
-
intr0x20entry
-
初始化了数组intr_entry_table
-
利用的编译器对section的data段的处理,但总感觉这个不是那么靠谱。
-
1.3 代码
结合了《Orange'S:一个操作系统的实现》与 《操作系统真相还原》这两本书的代码
-
cong@msi:/work/os/code/7int$ tree
-
.
-
├── boot
-
│ ├── loader.S
-
│ └── mbr.S
-
├── include
-
│ ├── interrupt.h
-
│ ├── io.h
-
│ ├── print.h
-
│ └── stdint.h
-
├── kernel
-
│ ├── interrupt.c
-
│ ├── kernel.S
-
│ └── main.c
-
├── lib
-
│ ├── printf.c
-
│ └── put_char.S
-
└── Makefile
1.3.1 在interrupt.c中定义
-
cong@msi:/work/os/code/7int$ cat kernel/interrupt.c
-
#include "interrupt.h"
-
static gate_desc idt[IDT_DESC_CNT];
-
-
void exception_handler(int vec_no,int err_code,int eip,int cs,int eflags)
-
{
-
printf("vec_no=%d,err_code=%d\n", vec_no, err_code);
-
printf("eip=0x%x,cs=0x%x,eflags=0x%x\n",eip, cs, eflags);
-
return ;
-
}
-
/* 初始化可编程中断控制器8259A */
-
static void pic_init(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);
-
-
printf("pic_init done\n");
-
}
-
/* 创建中断门描述符 */
-
static void make_idt_desc(gate_desc* p_gdesc, uint8_t attr, intr_handler function) {
-
p_gdesc->func_offset_low_word = (uint32_t)function & 0x0000FFFF; -->中断入口地址的低16位
-
p_gdesc->selector = SELECTOR_K_CODE; -->选择子统一为内核代码段的选择子
-
p_gdesc->dcount = 0;
-
p_gdesc->attr = attr;
-
p_gdesc->func_offset_high_word = ((uint32_t)function & 0xFFFF0000) >> 16; -->中断入口地址的高16位
-
}
-
-
//将中断处理函数的地址放进idt中,并初始化idt
-
static void idt_desc_init(void)
-
{
-
make_idt_desc(&idt[INT_VECTOR_DIVIDE], IDT_DESC_ATTR_DPL0, divide_error);
-
make_idt_desc(&idt[INT_VECTOR_DEBUG], IDT_DESC_ATTR_DPL0, single_step_exception);
-
make_idt_desc(&idt[INT_VECTOR_NMI], IDT_DESC_ATTR_DPL0, nmi);
-
make_idt_desc(&idt[INT_VECTOR_BREAKPOINT], IDT_DESC_ATTR_DPL0, breakpoint_exception);
-
make_idt_desc(&idt[INT_VECTOR_OVERFLOW], IDT_DESC_ATTR_DPL0, overflow);
-
make_idt_desc(&idt[INT_VECTOR_BOUNDS], IDT_DESC_ATTR_DPL0, bounds_check);
-
make_idt_desc(&idt[INT_VECTOR_INVAL_OP], IDT_DESC_ATTR_DPL0, inval_opcode);
-
make_idt_desc(&idt[INT_VECTOR_COPROC_NOT], IDT_DESC_ATTR_DPL0, copr_not_available);
-
make_idt_desc(&idt[INT_VECTOR_DOUBLE_FAULT], IDT_DESC_ATTR_DPL0, double_fault);
-
make_idt_desc(&idt[INT_VECTOR_COPROC_SEG], IDT_DESC_ATTR_DPL0, copr_seg_overrun);
-
make_idt_desc(&idt[INT_VECTOR_INVAL_TSS], IDT_DESC_ATTR_DPL0, inval_tss);
-
make_idt_desc(&idt[INT_VECTOR_SEG_NOT], IDT_DESC_ATTR_DPL0, segment_not_present);
-
make_idt_desc(&idt[INT_VECTOR_STACK_FAULT], IDT_DESC_ATTR_DPL0, stack_exception);
-
make_idt_desc(&idt[INT_VECTOR_PROTECTION], IDT_DESC_ATTR_DPL0, general_protection);
-
make_idt_desc(&idt[INT_VECTOR_PAGE_FAULT], IDT_DESC_ATTR_DPL0, page_fault);
-
make_idt_desc(&idt[INT_VECTOR_COPROC_ERR], IDT_DESC_ATTR_DPL0, copr_error);
-
//timer interrupt
-
make_idt_desc(&idt[INT_VECTOR_IRQ0], IDT_DESC_ATTR_DPL0, timer_int);
-
}
-
-
/* 完成有关中断的所有初始化工作*/
-
void idt_init(void)
-
{
-
idt_desc_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));
-
printf("idt_init done\n");
-
}
1.3.2 中断处理函数
-
cong@msi:/work/os/code/7int$ cat kernel/kernel.S
-
extern exception_handler
-
-
[bits 32]
-
[section .text] ; 代码在此
-
global divide_error
-
global single_step_exception
-
global nmi
-
global breakpoint_exception
-
global overflow
-
global bounds_check
-
global inval_opcode
-
global copr_not_available
-
global double_fault
-
global copr_seg_overrun
-
global inval_tss
-
global segment_not_present
-
global stack_exception
-
global general_protection
-
global page_fault
-
global copr_error
-
global timer_int
-
-
; 中断和异常 -- 异常
-
divide_error:
-
push 0xFFFFFFFF ; no err code
-
push 0 ; vector_no = 0
-
jmp exception
-
single_step_exception:
-
push 0xFFFFFFFF ; no err code
-
push 1 ; vector_no = 1
-
jmp exception
-
nmi:
-
push 0xFFFFFFFF ; no err code
-
push 2 ; vector_no = 2
-
jmp exception
-
breakpoint_exception:
-
push 0xFFFFFFFF ; no err code
-
push 3 ; vector_no = 3
-
jmp exception
-
overflow:
-
push 0xFFFFFFFF ; no err code
-
push 4 ; vector_no = 4
-
jmp exception
-
bounds_check:
-
push 0xFFFFFFFF ; no err code
-
push 5 ; vector_no = 5
-
jmp exception
-
inval_opcode:
-
push 0xFFFFFFFF ; no err code
-
push 6 ; vector_no = 6
-
jmp exception
-
copr_not_available:
-
push 0xFFFFFFFF ; no err code
-
push 7 ; vector_no = 7
-
jmp exception
-
double_fault:
-
push 8 ; vector_no = 8
-
jmp exception
-
copr_seg_overrun:
-
push 0xFFFFFFFF ; no err code
-
push 9 ; vector_no = 9
-
jmp exception
-
inval_tss:
-
push 10 ; vector_no = A
-
jmp exception
-
segment_not_present:
-
push 11 ; vector_no = B
-
jmp exception
-
stack_exception:
-
push 12 ; vector_no = C
-
jmp exception
-
general_protection:
-
push 13 ; vector_no = D
-
jmp exception
-
page_fault:
-
push 14 ; vector_no = E
-
jmp exception
-
copr_error:
-
push 0xFFFFFFFF ; no err code
-
push 16 ; vector_no = 10h
-
jmp exception
-
timer_int:
-
push 0xFFFFFFFF
-
push 32
-
jmp exception
-
-
exception:
-
call exception_handler
-
; 如果是从片上进入的中断,除了往从片上发送EOI外,还要往主片上发送EOI
-
mov al,0x20 ; 中断结束命令EOI
-
out 0xa0,al ; 向从片发送
-
out 0x20,al ; 向主片发送
-
add esp, 4*2 ; 让栈顶指向 EIP,堆栈中从顶向下依次是:EIP、CS、EFLAGS
-
iret
1.3.3 main中打开中断
-
cong@msi:/work/os/code/7int$ cat kernel/main.c
-
#include "print.h"
-
#include <interrupt.h>
-
extern void put_test(void);
-
int main(void)
-
{
-
int a=3;
-
put_char('k');
-
put_test();
-
printf("a=%d\n",a);
-
idt_init();
-
asm volatile("sti");
-
while(1)
-
{
-
}
-
return 0;
-
}
1.4
如果是hlt的话 c之后
(0) [0x000000001963] 0008:c0001963 (unk. ctxt): nop ; 6690
如果是iret的话 c之后
(0) [0x00000000153c] 0008:c000153c (unk. ctxt): jmp .-2 (0xc000153c) ; ebfe
阅读(1081) | 评论(0) | 转发(0) |