/*
* linux/kernel/asm.s
*
* (C) 1991 Linus Torvalds
*/
/*
* asm.s contains the low-level code for most hardware faults.
* page_exception is handled by the mm, so that isn't here. This
* file also handles (hopefully) fpu-exceptions due to TS-bit, as
* the fpu must be properly saved/resored. This hasn't been tested.
*/
//这里定义了一些low-level中断 int0-int16 (int 17-int 31 Intel保留的中断号) 大部分硬件中断
.globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op
.globl _double_fault,_coprocessor_segment_overrun
.globl _invalid_TSS,_segment_not_present,_stack_segment
.globl _general_protection,_coprocessor_error,_irq13,_reserved
//XCHG 交换(Exchange)
//XCHG OP1,OP2;将OP1和OP2的数据对换
//操作 X=OP1 OP1=OP2 OP2=X 补充说明:XCHG不影响标志位,OP中不能有段寄存器。
//LEA OPRD1,OPRD2
//功能: 将源操作数给出的有效地址传送到指定的的寄存器中.
_divide_error:
//note: do开头的函数是asm.s中断处理函数调用的c函数 在trap.c中定义
//下一篇博客 将分析trap.c
pushl $_do_divide_error //int 0 除零错误 将要调用的函数地址入栈
no_error_code:
xchgl %eax,(%esp) //取函数的入口地址
pushl %ebx //其他相关寄存器的入栈
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds
push %es
push %fs
//将出错码和中断返回地址入栈作为函数调用_do_divide_error的参数。
pushl $0 # "error code" 出错码
lea 44(%esp),%edx //do_divide_error(long esp,long error_code) 可见传的esp变量时要求
pushl %edx //出错位置 以便打印 下面的jmp error_code也是同一个意思
//初始化段选择子
movl $0x10,%edx
mov %dx,%ds
mov %dx,%es
mov %dx,%fs
call *%eax //调用操作数指定地址处的函数
addl $8,%esp //跳到%fs处
pop %fs
pop %es
pop %ds
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
//int1 debug调试中断 当eflags中的TF标志置位时发生中断
_debug:
pushl $_do_int3 # _do_debug
jmp no_error_code
//int2 非屏蔽中断调用入口点 当接收到一个nmi信号 CPU就会产生此中断
_nmi:
pushl $_do_nmi
jmp no_error_code
//断点指令的入口
_int3:
pushl $_do_int3
jmp no_error_code
//CPU OF位置位就会产生此中断
_overflow:
pushl $_do_overflow
jmp no_error_code
_bounds:
pushl $_do_bounds
jmp no_error_code
//int 6无效操作入口
_invalid_op:
pushl $_do_invalid_op
jmp no_error_code
//int 9 协处理器的出错
_coprocessor_segment_overrun:
pushl $_do_coprocessor_segment_overrun
jmp no_error_code
//int 15 Intel保留的中断的入口
_reserved:
pushl $_do_reserved
jmp no_error_code
//当协处理器执行完一个操作时 就会发出IRQ13中断的信号 以通知CPU操作完成
//在这里重点说明一下 8259A的中断结束方式 (要想深入一点了解 请参考 微机原理与接口技术)
//分为自动AEOI方式 和非自动结束方式 其实后者又可以分为 普通EOI方式和特殊SEOI方式
//这里用到的是普通EOI方式
//中断结束处理其实就是对中断服务器ISR中的对应为的处理 当一个中断得到响应是 8259A
//使ISR寄存器的对应位置置1 表明对应的外设正在服务 并为中断判优提供依据 中断结束时
//必须是ISR寄存器的对应位置置0 否则中断判优惠不正常 什么时刻使ISR中对应位置置0 就产生 不同的中断结束方式
/*普通EOI方式
8259A 结束中断的方式
mov al,0x20
out 0x20,al
EOI结束命令必须放在返回指令之前
说明:采用的多级级联的方式 那么从片也要发送EOI信号
*/
_irq13:
pushl %eax
xorb %al,%al
outb %al,$0xF0 //用于清忙锁存器 通过写该端口 本中断将消除CPU的BUSY延续信号 并重新激活80387的
//扩展请求引脚PEREQ
movb $0x20,%al //主片
outb %al,$0x20
jmp 1f //延迟一段时间
1: jmp 1f
1: outb %al,$0xA0 //从片
popl %eax
jmp _coprocessor_error
//中断嵌套 产生中断 linux中不允许中断嵌套
_double_fault:
pushl $_do_double_fault
error_code:
xchgl %eax,4(%esp) # error code <-> %eax
xchgl %ebx,(%esp) # &function <-> %ebx
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds
push %es
push %fs
pushl %eax # error code
lea 44(%esp),%eax # offset
pushl %eax //程序返回入口地址
movl $0x10,%eax //段选择子的改变
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
call *%ebx
addl $8,%esp //不要入栈的2个作为c函数的参数 供c语言使用 参考do_divide_error
pop %fs
pop %es
pop %ds
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
//int10 无效的任务状态栈
_invalid_TSS:
pushl $_do_invalid_TSS
jmp error_code
//int11 段不存在
_segment_not_present:
pushl $_do_segment_not_present
jmp error_code
//int12 堆栈错误 是int11 和 int13的特殊情况
_stack_segment:
pushl $_do_stack_segment
jmp error_code
//int13 一般保护错误
_general_protection:
pushl $_do_general_protection
jmp error_code
//int7 设备不存在异常 定义在sys_call.s中 在接下来的两篇博客会介绍
//int14 页错误 page.s中 在memroy.c中 我们已经提及过了
//int16 协处理器错误 sys_call.s
//int 0x20 时钟中断 (最重要的一个中断) 定义在sys_call.s中
//系统调用int 0x80 sys_call.s 相当于BIOS下函数的调用
转载于:https://my.oschina.net/lngligelang/blog/263020