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