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