int.s文件的核心功能是对中断进行设置,不过这里把一些中断处理程序也放进来,而且还把其他文件中用到的一些库函数放到这儿,目的是为了方便,不需再在lib/目录下重新建立asmlib.s类似的文件。由于该文件比较长,所有分两部分解析。
1 %include "asm/int.sh"
2
3 extern boot_heartbeat
4 extern pre_schedule
5 extern validate_buffer
6 extern sys_call_table
7 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8 ; 到处中断向量表和中断描述符表寄存器
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10 global idt
11 global idtr
12
13 global set_idt
14 global sys_call
15
16 global out_byte
17 global in_byte
18 global read_port
19 global write_port
20 global install_int_handler
21 global uninstall_int_handler
22 global install_sys_call_handler
23 global enable_hwint
24 global disable_hwint
25
26 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
27 ; 处理器能够处理的默认中断和异常
28 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
29 global divide_error
30 global debug_exception
31 global nmi
32 global breakpoint_exception
33 global overflow
34 global bounds_check
35 global inval_opcode
36 global copr_not_available
37 global double_fault
38 global copr_seg_overrun
39 global inval_tss
40 global segment_not_present
41 global stack_exception
42 global general_protection
43 global page_fault
44 global copr_error
45 global exception
46
47 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
48 ; 可屏蔽的硬件中断
49 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
50 global int_clock
51 global int_keyboard
52 global int_serial_port2
53 global int_serial_port1
54 global int_lpt2
55 global int_floppy
56 global int_lpt1
57 global int_rtc
58 global int_ps_2_mouse
59 global int_fpu_fault
60 global int_at_win
61 global int_default
62
63 [SECTION .text]
64 set_idt:
65 ; 对8259A主片写入ICW1
66 push 0x11
67 push MASTER_CTL_8259
68 call out_byte
69 add esp, 4 * 2
70
71 ; 对8259A从片写入ICW1
72 push 0x11
73 push SLAVE_CTL_8259
74 call out_byte
75 add esp, 4 * 2
76
77 ; 设置8259A主片的中断入口地址,为IRQ0_IV
78 push IRQ0_IV
79 push MASTER_CTL_MASK_8259
80 call out_byte
81 add esp, 4 * 2
82
83 ; 设置8259A从片的中断入口地址,为IRQ8_IV
84 push IRQ8_IV
85 push SLAVE_CTL_MASK_8259
86 call out_byte
87 add esp, 4 * 2
88
89 ; 向8259A主片写入ICW3,表明IR2处级联了从片
90 push 0x4
91 push MASTER_CTL_MASK_8259
92 call out_byte
93 add esp, 4 * 2
94
95 ; 向8259A从片写入ICW3,表明从片是连接在主片的IR2处
96 push 0x2
97 push SLAVE_CTL_MASK_8259
98 call out_byte
99 add esp, 4 * 2
100
101 ; 向8259A主片写入ICW4
102 push 0x1
103 push MASTER_CTL_MASK_8259
104 call out_byte
105 add esp, 4 * 2
106
107 ; 向8259A从片写入ICW4
108 push 0x1
109 push SLAVE_CTL_MASK_8259
110 call out_byte
111 add esp, 4 * 2
112
113 ; 屏蔽8259A主片的所有硬件中断
114 push 0xff
115 push MASTER_CTL_MASK_8259
116 call out_byte
117 add esp, 4 * 2
118
119 ; 屏蔽8259A从片的所有硬件中断
120 push 0xff
121 push SLAVE_CTL_MASK_8259
122 call out_byte
123 add esp, 4 * 2
124
125 ; 除零错误(内核态允许的中断)
126 ; 指令div or idiv
127 push PRIVILEGE_KERNEL
128 push divide_error
129 push INT_GATE_386
130 push DIVIDE_IV
131 call init_idt
132 add esp, 4 * 4
133
134 ; 调试异常(内核态允许的中断)
135 push PRIVILEGE_KERNEL
136 push debug_exception
137 push INT_GATE_386
138 push DEBUG_IV
139 call init_idt
140 add esp, 4 * 4
141
142 ; 非屏蔽中断(内核态允许的中断)
143 push PRIVILEGE_KERNEL
144 push nmi
145 push INT_GATE_386
146 push NMI_IV
147 call init_idt
148 add esp, 4 * 4
149
150 ; 调试断点异常(用户态允许的中断)
151 ; 指令int3
152 push PRIVILEGE_USER
153 push breakpoint_exception
154 push INT_GATE_386
155 push BREAKPOINT_IV
156 call init_idt
157 add esp, 4 * 4
158
159 ; 溢出异常(用户态允许的中断)
160 ; 指令into
161 push PRIVILEGE_USER
162 push overflow
163 push INT_GATE_386
164 push OVERFLOW_IV
165 call init_idt
166 add esp, 4 * 4
167
168 ; 越界错误(内核态允许的中断)
169 ; linux将其设置为用户态也允许的中断
170 ; 指令bound
171 push PRIVILEGE_KERNEL
172 push bounds_check
173 push INT_GATE_386
174 push BOUNDS_IV
175 call init_idt
176 add esp, 4 * 4
177
178 ; 无效操作码错误(内核态允许的中断)
179 ; 主要由ud2或无效指令引起
180 push PRIVILEGE_KERNEL
181 push inval_opcode
182 push INT_GATE_386
183 push INVAL_OP_IV
184 call init_idt
185 add esp, 4 * 4
186
187 ; 设备不可用/无数学协处理器(内核态允许的中断)
188 ; 浮点数或wait/fwait指令
189 push PRIVILEGE_KERNEL
190 push copr_not_available
191 push INT_GATE_386
192 push COPROC_NOT_IV
193 call init_idt
194 add esp, 4 * 4
195
196 ; 双重错误(内核态允许的中断)
197 ; 所有能产生异常或NMI或intr的指令
198 push PRIVILEGE_KERNEL
199 push double_fault
200 push INT_GATE_386
201 push DOUBLE_FAULT_IV
202 call init_idt
203 add esp, 4 * 4
204
205 ; 386机器不再产生此种异常
206 push PRIVILEGE_KERNEL
207 push copr_seg_overrun
208 push INT_GATE_386
209 push COPROC_SEG_IV
210 call init_idt
211 add esp, 4 * 4
212
213 ; 无效TSS错误(内核态允许的中断)
214 ; 任务切换或访问TSS段时
215 push PRIVILEGE_KERNEL
216 push inval_tss
217 push INT_GATE_386
218 push INVAL_TSS_IV
219 call init_idt
220 add esp, 4 * 4
221
222 ; 段不存在错误(内核态允许的中断)
223 ; 加载段寄存器或访问系统段时
224 push PRIVILEGE_KERNEL
225 push segment_not_present
226 push INT_GATE_386
227 push SEG_NOT_IV
228 call init_idt
229 add esp, 4 * 4
230
231 ; 堆栈段错误(内核态允许的中断)
232 ; 堆栈段操作或加载ss时
233 push PRIVILEGE_KERNEL
234 push stack_exception
235 push INT_GATE_386
236 push STACK_FAULT_IV
237 call init_idt
238 add esp, 4 * 4
239
240 ; 常规保护错误(内核态允许的中断)
241 ; 内存或其他保护检验时
242 push PRIVILEGE_KERNEL
243 push general_protection
244 push INT_GATE_386
245 push PROTECTION_IV
246 call init_idt
247 add esp, 4 * 4
248
249 ; 页错误(内核态允许的中断)
250 ; 内存访问时
251 push PRIVILEGE_KERNEL
252 push page_fault
253 push INT_GATE_386
254 push PAGE_FAULT_IV
255 call init_idt
256 add esp, 4 * 4
257
258
259 ;;;;;;;;;;;;;;;;注意这里0x0f号中断保留,未使用
260
261
262 ; x87FPU浮点错误(内核态允许的中断)
263 ; x87FPU浮点指令或WAIT/FWAIT指令
264 push PRIVILEGE_KERNEL
265 push copr_error
266 push INT_GATE_386
267 push COPROC_ERR_IV
268 call init_idt
269 add esp, 4 * 4
以上代码虽然比较长,但是任务很简单主要完成的有:
1、设置8259A,80X86架构里面内置有两片8259A,通过设置达到如下效果:分为主8259A和从8259A,从片连接在主片的IRQ2引脚上;设置主片的IRQ0引脚(时钟中断)的中断号为IRQ0_IV(0x20),从8259A的IRQ0引脚的中断号为IRQ8_IV;然后设置默认屏蔽所有的主片和从片的中断(这样是为了在初始化对应的硬件的时候再打开对应中断);
2、设置0~15号中断向量,这些中断向量都是Intel规定的中断;设置中断的核心函数是init_idt,该函数稍后会讲解。
1 ; 从0x20开始到中断向量表尾部,统一初始化成默认的中断处理程序
2 mov ecx, IRQ0_IV
3 push PRIVILEGE_KERNEL
4 push int_default
5 push INT_GATE_386
6 init_rest:
7 push ecx
8 call init_idt
9 pop ecx
10 inc ecx
11 cmp ecx, 255
12 jna init_rest
13 add esp, 4 * 3
14
15 ; 全部中断向量入口程序加载完成之后便加载中断描述符表
16 lidt [idtr] ; 加载中断描述符表
17 ret
18
19 init_idt:
20 mov eax, [esp + 4 * 1] ; 中断向量号
21 mov ebx, [esp + 4 * 2] ; 描述符类型(中断门/调用门/陷阱门)
22 mov ecx, [esp + 4 * 3] ; 中断处理程序入口
23 mov edx, [esp + 4 * 4] ; 特权级
24 mov esi, idt
25 shl eax, 3
26 add esi, eax ; 中断向量号乘以8然后加上idt基地址就能找到对用中断向量号的idt描述符
27 mov word [esi], cx
28 add esi, 2
29 mov word [esi], 0x8 ; CS段描述符
30 add esi, 2
31 mov byte [esi], 0x0
32 add esi, 1
33 shl edx, 5
34 and bl, 0x0f
35 or bl, 0x80
36 or bl, dl
37 mov byte [esi], bl
38 add esi, 1
39 shr ecx, 16
40 mov word [esi], cx
41 ret
42
43 ; 在发生中断时,eflags、cs、eip将自动被压入栈中
44 ; 如果有出错码的话,那么出错码紧接着继续被压入栈中(同样被自动压入栈中)
45 ; 如果有堆栈切换,也就是说有特权级变化,那么原ss和esp将被压入内层堆栈,之后才是eflags、cs、eip
46 ; 从中断或者异常中返回时必须用iretd,它与ret不同的时它会改变eflags的值
47 divide_error:
48 push 0xffffffff
49 push DIVIDE_IV
50 jmp exception
51
52 debug_exception:
53 push 0xffffffff
54 push DEBUG_IV
55 jmp exception
56
57 nmi:
58 push 0xffffffff
59 push NMI_IV
60 jmp exception
61
62 breakpoint_exception:
63 push 0xffffffff
64 push BREAKPOINT_IV
65 jmp exception
66
67 overflow:
68 push 0xffffffff
69 push OVERFLOW_IV
70 jmp exception
71
72 bounds_check:
73 push 0xffffffff
74 push BOUNDS_IV
75 jmp exception
76
77 inval_opcode:
78 push 0xffffffff
79 push INVAL_OP_IV
80 jmp exception
81
82 copr_not_available:
83 push 0xffffffff
84 push COPROC_NOT_IV
85 jmp exception
86
87 double_fault:
88 push DOUBLE_FAULT_IV
89 jmp exception
90
91 copr_seg_overrun:
92 push 0xffffffff
93 push COPROC_SEG_IV
94 jmp exception
95
96 inval_tss: ; 系统将出错码自动压栈
97 push INVAL_TSS_IV
98 jmp exception
99
100 segment_not_present: ; 系统将出错码自动压栈
101 push SEG_NOT_IV
102 jmp exception
103
104 stack_exception: ; 系统将出错码自动压栈
105 push STACK_FAULT_IV
106 jmp exception
107
108 general_protection: ; 系统将出错码自动压栈
109 push PROTECTION_IV
110 jmp $
111 jmp exception
112
113 page_fault: ; 系统将出错码自动压栈
114 push PAGE_FAULT_IV
115 jmp exception
116
117 copr_error:
118 push 0xffffffff
119 push COPROC_ERR_IV
120 jmp exception
121
122 exception:
123 add esp, 4 * 2 ; 跳过出错码和向量号
124 cli
125 hlt ; 我们目前不处理错误,只要出错就让机器hlt
126 ;iretd
127
上面这段代码主要完成的工作有:
1、初始化从0x20-0xff的所有中断向量,使得这些中断向量均指向默认的中断处理函数int_default;
2、init_idt函数的实现,该函数有如下形式:void init_idt(int iv, int privil, void *fun, int descr_type); 接受的四个参数依次是中断向量号,该中断特权级,中断处理函数,描述符类型(是中断门or调用门or陷阱门);该函数通过设置对应IDT中的描述符项的属性完成设置。
3、0x00-0x0f的中断处理函数的实现,其实这些函数都跳转到(不是调用,是直接跳转)exception函数,该函数直接将系统hlt,意思是我们的系统目前不支持例如除零错误等的异常中断。
posted on 2012-02-14 00:21
myjfm 阅读(529)
评论(0) 编辑 收藏 引用 所属分类:
操作系统