80dee1aff37a88af8ba536fc08a82471dbfd3dbe
[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 #include <arch/x86/interrupt.S.inc>
7
8 #define __ASM_INTR_DIAGNOSIS
9
10 #ifdef __ASM_INTR_DIAGNOSIS
11 .section .bss
12     .global debug_resv
13     debug_resv:
14         .skip 16
15     tmp_store:
16         .skip 4
17 #endif
18
19 .section .bss
20     .align 16
21     lo_tmp_stack:
22         .skip 256
23     tmp_stack:
24
25 /*
26     This perhaps the ugliest part in the project. 
27     It contains code to handle arbitrary depth of 
28     nested interrupt and all those corner cases and 
29     nasty gotchas.
30
31     Be aware the twists, offsets and hidden dependencies!
32
33 */
34
35 .section .text
36     .global interrupt_wrapper
37     interrupt_wrapper:
38         /*
39          Stack layout (layout of struct isr_param)
40     msa:   [ss]             > 76 -> 28
41            [esp]            > 72 -> 24
42            eflags           > 68 -> 20
43            cs               > 64 -> 16
44            eip              > 60 -> 12
45            err_code         > 56 -> 8
46            vector           > offset = 52 -> 4
47            [saved_prev_ctx] > offset = 0
48            ---
49            esp       > 12 * 4 = 48
50            gs
51            fs
52            es
53            ds         > offset = 8 * 4 = 32
54            esi
55            ebp
56            edi
57            edx
58            ecx
59            ebx
60            eax
61     lsa:   depth       > offset = 0
62
63             las: Least Significant Address
64             msa: Most Significant Address
65         */
66         cld
67
68         subl $4, %esp
69         pushl %esp
70
71         subl $16, %esp
72         movw %gs, 12(%esp)
73         movw %fs,  8(%esp)
74         movw %es,  4(%esp)
75         movw %ds,   (%esp)
76
77         pushl %esi
78         pushl %ebp
79         pushl %edi
80         pushl %edx
81         pushl %ecx
82         pushl %ebx
83         pushl %eax
84
85         movl __current, %eax
86         movl proc_intr_ctx(%eax), %eax
87         incl %eax
88         pushl %eax          # nested intr: current depth
89
90         movl ics(%esp), %eax   /* 取出 %cs */
91         andl $0x3, %eax          /* 判断 RPL */
92         jz 1f
93
94     /* crossing the user/kernel boundary */
95         movw $KDATA_SEG, %ax
96         movw %ax, %gs
97         movw %ax, %fs
98         movw %ax, %ds
99         movw %ax, %es
100
101         movl __current, %eax
102
103         # 保存用户栈顶指针。因为我们允许同级中断的产生,所以需要该手段跟踪用户栈的地址。
104         movl iuesp(%esp), %ebx     # 取出esp
105         movl %ebx, proc_ustack_top(%eax)     # 存入__current->ustack_top
106
107         # Save x87 context to user stack, rather than kernel's memory.
108         # XXX: what will happen if we triggered a page fault during fxsave?
109         movl iuesp(%esp), %eax
110         andl $stack_alignment, %eax 
111         subl $512, %eax
112         fxsave (%eax)
113
114     /* kernel space same-level switch */
115     1:
116         movl %esp, %eax
117         andl $stack_alignment, %esp
118         subl $16, %esp
119         movl %eax, (%esp)
120
121         call intr_handler
122
123         movl (%esp), %eax
124
125     .global soft_iret
126     soft_iret:
127         movl %eax, %esp
128
129 #ifdef __ASM_INTR_DIAGNOSIS
130         movl %eax, (debug_resv + 8)
131         movl iesp(%esp), %eax
132         movl exeip(%eax), %eax
133         movl %eax, (debug_resv + 4) # eip
134 #endif
135         // movl __current, %eax
136         // movl proc_fxstate(%eax), %eax
137         
138         // test %eax, %eax     # do we have stored x87 context?
139
140         movl ics(%esp), %eax
141         andl $3, %eax
142         jz 1f 
143
144         movl iuesp(%esp), %eax
145         andl $stack_alignment, %eax 
146         subl $512, %eax
147         fxrstor (%eax)
148
149 1:
150         popl %eax   # discard isr_param::depth
151         popl %eax
152         popl %ebx
153         popl %ecx
154         popl %edx
155         popl %edi
156         popl %ebp
157         popl %esi
158         
159         movw   (%esp), %ds
160         movw  4(%esp), %es
161         movw  8(%esp), %fs
162         movw 12(%esp), %gs
163
164         movl 16(%esp), %esp
165
166         movl %eax, tmp_store
167         movl __current, %eax
168         
169
170         # nested intr: restore saved context
171         popl proc_intr_ctx(%eax)
172
173         addl $8, %esp
174
175 #ifdef __ASM_INTR_DIAGNOSIS
176         movl (%esp), %eax
177         movl %eax, debug_resv
178 #endif
179         # 处理TSS.ESP的一些边界条件。如果是正常iret(即从内核模式*优雅地*退出)
180         # 那么TSS.ESP0应该为iret进行弹栈后,%esp的值。
181         # 所以这里的边界条件是:如返回用户模式,iret会额外弹出8个字节(ss,esp)
182         movl 4(%esp), %eax
183         andl $3, %eax
184         setnz %al
185         shll $3, %eax
186         addl $12, %eax
187         addl %esp, %eax
188         movl %eax, (_tss + tss_esp0_off)
189         movl tmp_store, %eax
190
191         iret
192
193     .global switch_to
194     switch_to:
195         # 约定
196         # arg1: 目标进程PCB地址 (next
197
198         popl %ebx               # next
199         movl __current, %eax    
200         movl proc_page_table(%eax), %ecx     # __current->pagetable
201         movl proc_page_table(%ebx), %eax     # next->pagetable
202         
203         cmpl %ecx, %eax         # if(next->pagtable != __current->pagetable) {
204         jz 1f
205         movl %eax, %cr3         #   cpu_lcr3(next->pagetable)
206                                 # }
207     1:
208         movl %ebx, __current    # __current = next
209
210         # 我们已经处在了新的地址空间,为了避免影响其先前的栈布局
211         # 需要使用一个临时的栈空间
212         movl $tmp_stack, %esp
213
214         call signal_dispatch    # kernel/signal.c
215
216         test %eax, %eax         # do we have signal to handle?
217         jz 1f
218
219         # 更新 tss
220         movl proc_intr_ctx(%ebx), %ecx      # __current->intr_ctx
221         movl %ecx, (tss_esp0_off + _tss)
222
223         jmp handle_signal
224     1:
225         movl proc_intr_ctx(%ebx), %eax
226         jmp soft_iret
227
228     .global handle_signal
229     handle_signal:
230         # 注意1:任何对proc_sig的布局改动,都须及时的保证这里的一致性!
231         # 注意2:handle_signal在调用之前,须确保proc_sig已经写入用户栈!
232         # arg1 in %eax: addr of proc_sig structure in user stack
233         movl psig_saved_ictx(%eax), %ebx      # %ebx = &proc_sig->saved_ictx
234
235         pushl $UDATA_SEG
236         pushl %eax              # esp
237
238         movl iexecp(%ebx), %ebx
239         pushl exeflags(%ebx)          # proc_sig->saved_ictx->execp->eflags
240         
241         pushl $UCODE_SEG        # cs
242         pushl psig_sigact(%eax)           # %eip = proc_sig->sigact
243
244         movw $UDATA_SEG, %cx    # switch data seg to user mode
245         movw %cx, %es
246         movw %cx, %ds
247         movw %cx, %fs
248         movw %cx, %gs
249
250         iret