3007d453a4c307eec7407c42504ad36ef2ad055c
[lunaix-os.git] / lunaix-os / kernel / asm / x86 / interrupt.S
1 #define __ASM__
2 #include <arch/x86/interrupts.h>
3 #include <arch/x86/i386_asm.h>
4 #include <arch/x86/tss.h>
5 #include <lunaix/syscall.h>
6 #define __ASM_INTR_DIAGNOSIS
7
8 #ifdef __ASM_INTR_DIAGNOSIS
9 .section .bss
10     .global debug_resv
11     debug_resv:
12         .skip 16
13     tmp_store:
14         .skip 4
15 #endif
16
17 .section .bss
18     .align 16
19     lo_tmp_stack:
20         .skip 256
21     tmp_stack:
22
23 /*
24     This perhaps the ugliest part in the project. 
25     It contains code to handle arbitrary depth of 
26     nested interrupt and all those corner cases and 
27     nasty gotchas.
28
29     Be aware the twists, offsets and hidden dependencies!
30
31 */
32
33 #define regsize 4
34
35 /* stack layout: saved interrupt context */
36     .struct 0
37 idepth:
38     .struct idepth + regsize
39 ieax:
40     .struct ieax + regsize
41 iebx:
42     .struct iebx + regsize
43 iecx:
44     .struct iecx + regsize
45 iedx:
46     .struct iedx + regsize
47 iedi:
48     .struct iedi + regsize
49 iebp:
50     .struct iebp + regsize
51 iesi:
52     .struct iesi + regsize
53 ids:
54     .struct ids + regsize
55 ies:
56     .struct ies + regsize
57 ifs:
58     .struct ifs + regsize
59 igs:
60     .struct igs + regsize
61 iesp:
62     .struct iesp + regsize
63 isave_prev:
64     .struct isave_prev + regsize
65 ivec:
66     .struct ivec + regsize
67 iecode:
68     .struct iecode + regsize
69 ieip:
70     .struct ieip + regsize
71 ics:
72     .struct ics + regsize
73 ieflags:
74     .struct ieflags + regsize
75 iuesp:
76     .struct iuesp + regsize
77 iuss:
78
79
80 /* stack layout: execution (flow-control) state context */
81     .struct 0
82 exsave_prev:
83     .struct exsave_prev + regsize
84 exvec:
85     .struct exvec + regsize
86 execode:
87     .struct execode + regsize
88 exeip:
89     .struct exeip + regsize
90 excs:
91     .struct excs + regsize
92 exeflags:
93     .struct exeflags + regsize
94 exuesp:
95     .struct exuesp + regsize
96 exuss:
97
98 /* struct layout: critical section of struct proc_info */
99     .struct 0
100 proc_pid:
101     .struct proc_pid + regsize
102 proc_parent:
103     .struct proc_parent + regsize
104 proc_intr_ctx:
105     .struct proc_intr_ctx + regsize
106 proc_ustack_top:
107     .struct proc_ustack_top + regsize
108 proc_page_table:
109     .struct proc_page_table + regsize
110 proc_fxstate:
111
112 .section .text
113     .global interrupt_wrapper
114     interrupt_wrapper:
115         /*
116          Stack layout (layout of struct isr_param)
117     msa:   [ss]             > 76 -> 28
118            [esp]            > 72 -> 24
119            eflags           > 68 -> 20
120            cs               > 64 -> 16
121            eip              > 60 -> 12
122            err_code         > 56 -> 8
123            vector           > offset = 52 -> 4
124            [saved_prev_ctx] > offset = 0
125            ---
126            esp       > 12 * 4 = 48
127            gs
128            fs
129            es
130            ds         > offset = 8 * 4 = 32
131            esi
132            ebp
133            edi
134            edx
135            ecx
136            ebx
137            eax
138     lsa:   depth       > offset = 0
139
140             las: Least Significant Address
141             msa: Most Significant Address
142         */
143         cld
144
145         subl $4, %esp
146         pushl %esp
147
148         subl $16, %esp
149         movw %gs, 12(%esp)
150         movw %fs,  8(%esp)
151         movw %es,  4(%esp)
152         movw %ds,   (%esp)
153
154         pushl %esi
155         pushl %ebp
156         pushl %edi
157         pushl %edx
158         pushl %ecx
159         pushl %ebx
160         pushl %eax
161
162         movl __current, %eax
163         movl proc_intr_ctx(%eax), %eax
164         incl %eax
165         pushl %eax          # nested intr: current depth
166
167         movl ics(%esp), %eax   /* 取出 %cs */
168         andl $0x3, %eax          /* 判断 RPL */
169         jz 1f
170
171         movw $KDATA_SEG, %ax    /* 如果从用户模式转来,则切换至内核数据段 */
172         movw %ax, %gs
173         movw %ax, %fs
174         movw %ax, %ds
175         movw %ax, %es
176
177         movl __current, %eax
178
179         # FIXME: Save x87 context to user stack, rather than kernel's memory.
180         # 保存x87FPU的状态
181         movl proc_fxstate(%eax), %ebx
182         fxsave (%ebx)
183
184         # 保存用户栈顶指针。因为我们允许同级中断的产生,所以需要该手段跟踪用户栈的地址。
185         movl iuesp(%esp), %ebx     # 取出esp
186         movl %ebx, proc_ustack_top(%eax)     # 存入__current->ustack_top
187
188     1:
189         movl %esp, %eax
190         andl $0xfffffff0, %esp
191         subl $16, %esp
192         movl %eax, (%esp)
193
194         call intr_handler
195
196         movl (%esp), %eax
197
198     .global soft_iret
199     soft_iret:
200         movl %eax, %esp
201
202 #ifdef __ASM_INTR_DIAGNOSIS
203         movl %eax, (debug_resv + 8)
204         movl iesp(%esp), %eax
205         movl exeip(%eax), %eax
206         movl %eax, (debug_resv + 4) # eip
207 #endif
208         movl __current, %eax
209         movl proc_fxstate(%eax), %eax
210         
211         test %eax, %eax     # do we have stored x87 context?
212         jz 1f 
213         fxrstor (%eax) 
214
215 1:
216         popl %eax   # discard isr_param::depth
217         popl %eax
218         popl %ebx
219         popl %ecx
220         popl %edx
221         popl %edi
222         popl %ebp
223         popl %esi
224         
225         movw   (%esp), %ds
226         movw  4(%esp), %es
227         movw  8(%esp), %fs
228         movw 12(%esp), %gs
229
230         movl 16(%esp), %esp
231
232         movl %eax, tmp_store
233         movl __current, %eax
234         
235
236         # nested intr: restore saved context
237         popl proc_intr_ctx(%eax)
238
239         addl $8, %esp
240
241 #ifdef __ASM_INTR_DIAGNOSIS
242         movl (%esp), %eax
243         movl %eax, debug_resv
244 #endif
245         # 处理TSS.ESP的一些边界条件。如果是正常iret(即从内核模式*优雅地*退出)
246         # 那么TSS.ESP0应该为iret进行弹栈后,%esp的值。
247         # 所以这里的边界条件是:如返回用户模式,iret会额外弹出8个字节(ss,esp)
248         movl 4(%esp), %eax
249         andl $3, %eax
250         setnz %al
251         shll $3, %eax
252         addl $12, %eax
253         addl %esp, %eax
254         movl %eax, (_tss + tss_esp0_off)
255         movl tmp_store, %eax
256
257         iret
258
259     .global switch_to
260     switch_to:
261         # 约定
262         # arg1: 目标进程PCB地址 (next
263
264         popl %ebx               # next
265         movl __current, %eax    
266         movl proc_page_table(%eax), %ecx     # __current->pagetable
267         movl proc_page_table(%ebx), %eax     # next->pagetable
268         
269         cmpl %ecx, %eax         # if(next->pagtable != __current->pagetable) {
270         jz 1f
271         movl %eax, %cr3         #   cpu_lcr3(next->pagetable)
272                                 # }
273     1:
274         movl %ebx, __current    # __current = next
275
276         # 我们已经处在了新的地址空间,为了避免影响其先前的栈布局
277         # 需要使用一个临时的栈空间
278         movl $tmp_stack, %esp
279         
280         # 更新 tss
281         movl proc_intr_ctx(%ebx), %eax      # proc->intr_ctx
282         movl iesp(%eax), %eax      # intr_ctx->esp
283         movl %eax, (tss_esp0_off + _tss)
284
285         call signal_dispatch    # kernel/signal.c
286
287         test %eax, %eax         # do we have signal to handle?
288         jz 1f
289         jmp handle_signal
290     1:
291         movl proc_intr_ctx(%ebx), %eax
292         jmp soft_iret
293
294     .global handle_signal
295     handle_signal:
296         # 注意1:任何对proc_sig的布局改动,都须及时的保证这里的一致性!
297         # 注意2:handle_signal在调用之前,须确保proc_sig已经写入用户栈!
298         # arg1 in %eax: addr of proc_sig structure in user stack
299         leal 12(%eax), %ebx      # %ebx = &proc_sig->prev_context
300
301         pushl $UDATA_SEG        # proc_sig->prev_context.proc_regs.ss
302         pushl %eax              # esp
303
304         movl 48(%ebx), %ebx
305         pushl 68(%ebx)          # proc_sig->prev_context.proc_regs.execp->eflags
306         
307         pushl $UCODE_SEG        # cs
308         pushl 4(%eax)           # %eip = proc_sig->sigact
309
310         movw $UDATA_SEG, %cx    # switch data seg to user mode
311         movw %cx, %es
312         movw %cx, %ds
313         movw %cx, %fs
314         movw %cx, %gs
315
316         iret