refactor: one more step towards arch-agnostic design
[lunaix-os.git] / lunaix-os / arch / i386 / exceptions / interrupt.S
1 #define __ASM__
2 #include <sys/interrupts.h>
3 #include <sys/abi.h>
4 #include <sys/interrupt.S.inc>
5
6 #include <lunaix/syscall.h>
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         cld
39
40         subl $4, %esp
41         pushl %esp
42
43         subl $16, %esp
44         movw %gs, 12(%esp)
45         movw %fs,  8(%esp)
46         movw %es,  4(%esp)
47         movw %ds,   (%esp)
48
49         pushl %esi
50         pushl %ebp
51         pushl %edi
52         pushl %edx
53         pushl %ecx
54         pushl %ebx
55         pushl %eax
56
57         movl __current, %eax
58         movl proc_intr_ctx(%eax), %eax
59         incl %eax
60         pushl %eax          # nested intr: current depth
61
62         movl ics(%esp), %eax   /* 取出 %cs */
63         andl $0x3, %eax          /* 判断 RPL */
64         jz 1f
65
66     /* crossing the user/kernel boundary */
67         movw $KDATA_SEG, %ax
68         movw %ax, %gs
69         movw %ax, %fs
70         movw %ax, %ds
71         movw %ax, %es
72
73         movl __current, %ebx
74
75         # Save x87 context to user stack, rather than kernel's memory.
76         # XXX what will happen if we triggered a page fault during fxsave?
77         # FIXME can we remove this overhead?
78         movl iuesp(%esp), %eax
79         andl $stack_alignment, %eax 
80         subl $512, %eax
81         fxsave (%eax)
82
83         # 保存用户栈顶指针。因为我们允许同级中断的产生,所以需要该手段跟踪用户栈的地址。
84         movl %eax, proc_ustack_top(%ebx)     # 存入__current->ustack_top
85
86     /* kernel space same-level switch */
87     1:
88         movl %esp, %eax
89         andl $stack_alignment, %esp
90         subl $16, %esp
91         movl %eax, (%esp)
92
93         call intr_handler
94
95         movl (%esp), %eax
96
97     .global soft_iret
98     soft_iret:
99         movl %eax, %esp
100
101 #ifdef __ASM_INTR_DIAGNOSIS
102         movl %eax, (debug_resv + 8)
103         movl iesp(%esp), %eax
104         movl exeip(%eax), %eax
105         movl %eax, (debug_resv + 4) # eip
106 #endif
107
108         movl ics(%esp), %eax
109         andl $3, %eax
110         jz 1f 
111
112         movl __current, %eax
113         movl proc_ustack_top(%eax), %eax
114         test %eax, %eax
115         jz 1f
116         fxrstor (%eax)
117
118 1:
119         popl %eax   # discard isr_param::depth
120         popl %eax
121         popl %ebx
122         popl %ecx
123         popl %edx
124         popl %edi
125         popl %ebp
126         popl %esi
127         
128         movw   (%esp), %ds
129         movw  4(%esp), %es
130         movw  8(%esp), %fs
131         movw 12(%esp), %gs
132
133         movl 16(%esp), %esp
134
135         movl %eax, tmp_store
136         movl __current, %eax
137
138         # nested intr: restore saved context
139         popl proc_intr_ctx(%eax)
140
141         addl $8, %esp
142
143 #ifdef __ASM_INTR_DIAGNOSIS
144         movl (%esp), %eax
145         movl %eax, debug_resv
146 #endif
147         # 处理TSS.ESP的一些边界条件。如果是正常iret(即从内核模式*优雅地*退出)
148         # 那么TSS.ESP0应该为iret进行弹栈后,%esp的值。
149         # 所以这里的边界条件是:如返回用户模式,iret会额外弹出8个字节(ss,esp)
150         movl 4(%esp), %eax
151         andl $3, %eax
152         setnz %al
153         shll $3, %eax
154         addl $12, %eax
155         addl %esp, %eax
156         movl %eax, (_tss + tss_esp0_off)
157         movl tmp_store, %eax
158
159         iret
160
161     .global switch_to
162     switch_to:
163         # 约定
164         # arg1: 目标进程PCB地址 (next
165
166         movl %eax, %ebx               # next
167         movl __current, %eax    
168         movl proc_page_table(%eax), %ecx     # __current->pagetable
169         movl proc_page_table(%ebx), %eax     # next->pagetable
170         
171         cmpl %ecx, %eax         # if(next->pagtable != __current->pagetable) {
172         jz 1f
173         movl %eax, %cr3         #   cpu_lcr3(next->pagetable)
174                                 # }
175     1:
176         movl %ebx, __current    # __current = next
177
178         # 我们已经处在了新的地址空间,为了避免影响其先前的栈布局
179         # 需要使用一个临时的栈空间
180         movl $tmp_stack, %esp
181
182         call signal_dispatch    # kernel/signal.c
183
184         test %eax, %eax         # do we have signal to handle?
185         jz 1f
186
187         /*
188             将tss.esp0设置为上次调度前的esp值。
189             当处理信号时,上下文信息是不会恢复的,而是保存在用户栈中,然后直接跳转进位于用户空间的sig_wrapper进行
190             信号的处理。当用户自定义的信号处理函数返回时,sigreturn的系统调用才开始进行上下文的恢复(或者说是进行
191             另一次调度。
192             由于这中间没有进行地址空间的交换,所以第二次跳转使用的是同一个内核栈,而之前默认tss.esp0的值是永远指向最顶部
193             这样一来就有可能会覆盖更早的上下文信息(比如嵌套的信号捕获函数)
194         */
195         movl proc_intr_ctx(%ebx), %ecx      # __current->intr_ctx
196         movl %ecx, (tss_esp0_off + _tss)
197
198         jmp handle_signal
199
200     1:
201         movl proc_intr_ctx(%ebx), %eax
202         jmp soft_iret
203
204     .global handle_signal
205     handle_signal:
206         # 注意1:任何对proc_sig的布局改动,都须及时的保证这里的一致性!
207         # 注意2:handle_signal在调用之前,须确保proc_sig已经写入用户栈!
208         # arg1 in %eax: addr of proc_sig structure in user stack
209         movl psig_saved_ictx(%eax), %ebx      # %ebx = &proc_sig->saved_ictx
210
211         pushl $UDATA_SEG
212         pushl %eax              # esp
213
214         movl iexecp(%ebx), %ebx
215         pushl exeflags(%ebx)          # proc_sig->saved_ictx->execp->eflags
216         
217         pushl $UCODE_SEG        # cs
218         pushl psig_sigact(%eax)           # %eip = proc_sig->sigact
219
220         movw $UDATA_SEG, %cx    # switch data seg to user mode
221         movw %cx, %es
222         movw %cx, %ds
223         movw %cx, %fs
224         movw %cx, %gs
225
226         iret