58c033a2ffd34c7cfdb0635b341cabf11c5615dd
[lunaix-os.git] / lunaix-os / arch / x86 / exceptions / interrupt32.S
1 #define __ASM__
2 #include <sys/hart.h>
3 #include <sys/abi.h>
4 #include <sys/interrupt32.S.inc>
5
6 #include <lunaix/syscall.h>
7
8 #define tss_esp0_off 4
9 #define __ASM_INTR_DIAGNOSIS
10
11 #ifdef __ASM_INTR_DIAGNOSIS
12 .section .bss
13     .global debug_resv
14     debug_resv:
15         .skip 16
16     tmp_store:
17         .skip 4
18 #endif
19
20 .section .bss
21     .align 16
22     lo_tmp_stack:
23         .skip 256
24     tmp_stack:
25
26 /*
27     This perhaps the ugliest part in the project. 
28     It contains code to handle arbitrary depth of 
29     nested interrupt and all those corner cases and 
30     nasty gotchas.
31
32     Be aware the twists, offsets and hidden dependencies!
33
34 */
35
36 .section .text
37     .type interrupt_wrapper, @function
38     .global interrupt_wrapper
39     interrupt_wrapper:
40         cld
41
42         subl $4, %esp       # prealloc slot for parent linkage
43         pushl %esp
44
45         subl $16, %esp
46         movw %gs, 12(%esp)
47         movw %fs,  8(%esp)
48         movw %es,  4(%esp)
49         movw %ds,   (%esp)
50
51         pushl %esi
52         pushl %ebp
53         pushl %edi
54         pushl %edx
55         pushl %ecx
56         pushl %ebx
57         pushl %eax
58
59         pushl $0      // placeholder for depth accounting
60
61         movl ics(%esp), %eax   /* 取出 %cs */
62         andl $0x3, %eax          /* 判断 RPL */
63         jz 1f
64
65     /* crossing the user/kernel boundary */
66         movw $KDATA_SEG, %ax
67         movw %ax, %gs
68         movw %ax, %fs
69         movw %ax, %ds
70         movw %ax, %es
71
72         movl current_thread, %ebx
73         movl iuesp(%esp), %eax
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 I think we should defer this to scheduler, and pratice lazy save/load
78         #       Doing this will also make it safe from nested interrupt due to potential 
79         #       page fault when saving
80         # FIXME Also, generalise it to any FPU context, without constraining it to x87.
81
82         #andl $stack_alignment, %eax 
83         #subl $512, %eax
84         #fxsave (%eax)
85
86         # 保存用户栈顶指针。因为我们允许同级中断的产生,所以需要该手段跟踪用户栈的地址。
87         movl %eax, thread_ustack_top(%ebx)     # 存入__current->ustack_top
88
89     /* kernel space same-level switch */
90     1:
91         movl %esp, %eax
92         andl $stack_alignment, %esp
93         subl $16, %esp
94         movl %eax, (%esp)
95
96         xorl %ebp, %ebp     # marks the boundary of stack walking
97         call intr_handler
98
99         movl (%esp), %eax
100
101     .global soft_iret
102     soft_iret:
103         movl %eax, %esp
104
105 #ifdef __ASM_INTR_DIAGNOSIS
106         movl %eax, (debug_resv + 8)
107         movl iesp(%esp), %eax
108         movl exeip(%eax), %eax
109         movl %eax, (debug_resv + 4) # eip
110 #endif
111
112         movl ics(%esp), %eax
113         andl $3, %eax
114         jz 1f 
115
116         # # FIXME x87 fpu context 
117         # movl current_thread, %eax
118         # movl thread_ustack_top(%eax), %eax
119         # test %eax, %eax
120         # jz 1f
121         # fxrstor (%eax)
122
123 1:
124         popl %eax   # discard struct hart_state::depth
125         popl %eax
126         popl %ebx
127         popl %ecx
128         popl %edx
129         popl %edi
130         popl %ebp
131         popl %esi
132         
133         movw   (%esp), %ds
134         movw  4(%esp), %es
135         movw  8(%esp), %fs
136         movw 12(%esp), %gs
137
138         movl 16(%esp), %esp
139
140         movl %eax, tmp_store
141         movl current_thread, %eax
142
143         # nested intr: restore saved context
144         popl thread_hstate(%eax)
145
146         addl $8, %esp
147
148 #ifdef __ASM_INTR_DIAGNOSIS
149         movl (%esp), %eax
150         movl %eax, debug_resv
151 #endif
152         # 处理TSS.ESP的一些边界条件。如果是正常iret(即从内核模式*优雅地*退出)
153         # 那么TSS.ESP0应该为iret进行弹栈后,%esp的值。
154         # 所以这里的边界条件是:如返回用户模式,iret会额外弹出8个字节(ss,esp)
155         movl 4(%esp), %eax
156         andl $3, %eax
157         setnz %al
158         shll $3, %eax
159         addl $12, %eax
160         addl %esp, %eax
161         movl %eax, (_tss + tss_esp0_off)
162         movl tmp_store, %eax
163
164         iret
165
166     .type do_switch, @function
167     .global do_switch
168     do_switch:
169         # Assumption: __current already hold the target process
170
171         call proc_vmroot
172
173         movl %eax, %ebx
174         movl %cr3, %eax
175         xorl %ebx, %eax         # avoid setting cr3 if just local thread switch.
176         jz 1f
177
178         movl %ebx, %cr3         
179
180     1:
181         # the address space could be changed. A temporary stack
182         # is required to prevent corrupt existing stack
183         movl $tmp_stack, %esp
184
185         call signal_dispatch    # kernel/signal.c
186
187         movl current_thread, %ebx
188         test %eax, %eax         # do we have signal to handle?
189         jz 1f
190
191         /*
192             将tss.esp0设置为上次调度前的esp值。
193             当处理信号时,上下文信息是不会恢复的,而是保存在用户栈中,然后直接跳转进位于用户空间的sig_wrapper进行
194             信号的处理。当用户自定义的信号处理函数返回时,sigreturn的系统调用才开始进行上下文的恢复(或者说是进行
195             另一次调度。
196             由于这中间没有进行地址空间的交换,所以第二次跳转使用的是同一个内核栈,而之前默认tss.esp0的值是永远指向最顶部
197             这样一来就有可能会覆盖更早的上下文信息(比如嵌套的信号捕获函数)
198         */
199         pushl %eax
200         call update_tss
201
202         popl %eax
203         jmp handle_signal
204
205     1:
206         movl thread_hstate(%ebx), %eax
207         jmp soft_iret
208
209     .type handle_signal, @function 
210     .global handle_signal
211     handle_signal:
212         # 注意1:任何对proc_sig的布局改动,都须及时的保证这里的一致性!
213         # 注意2:handle_signal在调用之前,须确保proc_sig已经写入用户栈!
214         # arg1 in %eax: addr of proc_sig structure in user stack
215         movl psig_saved_hstate(%eax), %ebx      # %ebx = &proc_sig->saved_hstate
216
217         pushl $UDATA_SEG
218         pushl %eax              # esp
219
220         movl iexecp(%ebx), %ebx
221         pushl exeflags(%ebx)          # proc_sig->saved_hstate->execp->eflags
222         
223         pushl $UCODE_SEG        # cs
224         pushl psig_sigact(%eax)           # %eip = proc_sig->sigact
225
226         movw $UDATA_SEG, %cx    # switch data seg to user mode
227         movw %cx, %es
228         movw %cx, %ds
229         movw %cx, %fs
230         movw %cx, %gs
231
232         iret