18 |
#ifndef INTERRUPT_BASE_OFFSET |
#ifndef INTERRUPT_BASE_OFFSET |
19 |
# define INTERRUPT_BASE_OFFSET 0x60 |
# define INTERRUPT_BASE_OFFSET 0x60 |
20 |
#endif |
#endif |
|
|
|
|
#define INTERRUPT_BASE_OFFSET 0x60 |
|
21 |
|
|
22 |
#include "cpus.h" |
#include "cpus.h" |
23 |
#include "i386-asm.h" |
#include "i386-asm.h" |
24 |
#include "i386-segment.h" |
#include "i386-segment.h" |
25 |
#include "i386-trap.h" |
#include "i386-trap.h" |
26 |
#include "i386-syms.h" |
#include "i386-syms.h" |
|
|
|
|
#define R_CS 60 |
|
|
#define R_EFLAGS 64 |
|
|
#define R_EAX 44 |
|
|
#define R_EBX 32 |
|
|
#define R_ECX 40 |
|
|
#define R_TRAPNO 48 |
|
|
#define R_ERR 52 |
|
|
#define R_EIP 56 |
|
|
#define R_CR2 28 |
|
|
#define R_UESP 68 |
|
|
|
|
|
#define KSS_EBX 0 |
|
|
#define KSS_ESP 4 |
|
|
#define KSS_EBP 8 |
|
|
#define KSS_ESI 16 |
|
|
#define KSS_EDI 12 |
|
|
#define KSS_EIP 20 |
|
27 |
|
|
28 |
/* All interrupts enter here. |
.bss |
29 |
old %eax on stack; interrupt number in %eax. */ |
LEXT(kernel_stacks) |
30 |
|
.fill 4, NCPUS |
31 |
|
|
32 |
|
|
33 |
|
/* Interrupt exception point. |
34 |
|
|
35 |
|
Interrupt number and null error code on stack. |
36 |
|
|
37 |
|
?? interrupts run on kernel stack if we interrupted a |
38 |
|
user mode thread. */ |
39 |
|
|
40 |
.text |
.text |
|
.globl allintrs |
|
41 |
allintrs: |
allintrs: |
42 |
pusha |
pusha |
43 |
|
|
44 |
int_push_segs: |
int_push_segs: |
45 |
pushl %ds |
pushl %ds |
46 |
pushl %es |
pushl %es |
47 |
pushl %fs |
pushl %fs |
48 |
pushl %gs |
pushl %gs |
49 |
|
cld |
50 |
|
|
51 |
int_set_segs: |
int_set_segs: |
52 |
cld |
/* Set segment registers. */ |
53 |
jmp int_from_intstack |
|
54 |
|
mov %ss,%dx |
55 |
|
mov %dx,%ds |
56 |
|
mov %dx,%es |
57 |
|
mov %dx,%gs |
58 |
|
mov %dx,%fs |
59 |
|
|
60 |
|
/* Check if we interrupted a user mode thread. */ |
61 |
|
|
62 |
|
testb $3,R_CS(%esp) |
63 |
|
jz int_from_intstack |
64 |
|
|
65 |
|
int_from_usermode: |
66 |
|
|
67 |
|
/* Switch to kernel stack. */ |
68 |
|
|
69 |
|
CPU_NUMBER(%edx) |
70 |
|
movl CX(kernel_stacks,%edx),%ebx |
71 |
|
xchgl %ebx,%esp |
72 |
|
|
73 |
|
/* Call machine specific interrupt handler. */ |
74 |
|
|
75 |
|
movl R_TRAPNO(%ebx), %eax |
76 |
|
pushl %ebx |
77 |
|
call machine_interrupt |
78 |
|
popl %esp |
79 |
|
|
80 |
|
return_from_int: |
81 |
|
popl %gs |
82 |
|
popl %fs |
83 |
|
popl %es |
84 |
|
popl %ds |
85 |
|
popa |
86 |
|
addl $8,%esp |
87 |
|
iret |
88 |
|
|
89 |
int_from_intstack: |
int_from_intstack: |
90 |
|
/* Interrupt came from kernel mode. */ |
91 |
|
|
92 |
movl R_TRAPNO(%esp), %eax |
movl R_TRAPNO(%esp), %eax |
93 |
movl %esp, %ebx |
pushl $return_from_int |
|
|
|
|
pushl $_return_from_kernel |
|
94 |
jmp machine_interrupt |
jmp machine_interrupt |
95 |
|
|
96 |
|
|
97 |
/* Machine specific part of interrupt handler. This will ack the |
/* Machine specific part of interrupt handler. This will ack the |
98 |
interrupt. ??? is this correct? */ |
interrupt. ??? is this correct? */ |
99 |
|
|
100 |
.align 4 |
.align 4 |
101 |
machine_interrupt: |
machine_interrupt: |
102 |
|
|
103 |
|
/* Save interrupt number on stack. */ |
104 |
pushl %eax |
pushl %eax |
105 |
cmpl $8, %eax |
cmpl $8, %eax |
106 |
jae _second_pic |
jae .Lsecondary_pic |
107 |
|
|
108 |
movl $0x20, %edx |
movl $0x20, %edx |
109 |
movb $0x20, %al |
movb $0x20, %al |
110 |
outb %al, %dx |
outb %al, %dx |
111 |
jmp _take_interrupt |
jmp .Ltake_intr |
112 |
_second_pic: |
|
113 |
|
.Lsecondary_pic: |
114 |
|
/* Send EOI to both primary and secondary PIC. */ |
115 |
|
|
116 |
movl $0x20, %edx |
movl $0x20, %edx |
117 |
movb $0x20, %al |
movb $0x20, %al |
118 |
outb %al, %dx |
outb %al, %dx |
121 |
movb $0x20, %al |
movb $0x20, %al |
122 |
outb %al, %dx |
outb %al, %dx |
123 |
|
|
124 |
_take_interrupt: |
|
125 |
|
/* Take interrupt. |
126 |
|
We optimize for the case with timer interrupt. */ |
127 |
|
|
128 |
|
.Ltake_intr: |
129 |
popl %eax |
popl %eax |
130 |
cmpl $0, %eax |
cmpl $0, %eax |
131 |
jne _take_generic_interrupt |
jne _take_generic_interrupt |
132 |
|
|
133 |
|
#if 0 |
134 |
|
movl R_CS(%esp), %eax |
135 |
|
andl $3, %eax |
136 |
|
pushl %eax |
137 |
|
call timer_interrupt |
138 |
|
addl $4, %esp |
139 |
|
ret |
140 |
|
#else |
141 |
testb $3,R_CS(%esp) |
testb $3,R_CS(%esp) |
142 |
jnz _clock_from_kernel |
jnz _clock_from_kernel |
143 |
jmp _clock_from_user |
jmp _clock_from_user |
144 |
|
#endif |
145 |
|
|
146 |
_clock_from_kernel: |
_clock_from_kernel: |
147 |
pushl $0 |
pushl $0 |
165 |
call *%ebx |
call *%ebx |
166 |
addl $8, %esp |
addl $8, %esp |
167 |
0: ret |
0: ret |
168 |
|
|
169 |
|
|
170 |
|
|
171 |
|
.text |
172 |
|
.globl machine_syscall |
173 |
|
machine_syscall: |
174 |
|
pushl $0 |
175 |
|
pushl $0 |
176 |
|
jmp alltraps |
177 |
|
|
178 |
|
|
179 |
|
/* System call entry point. |
180 |
|
|
181 |
|
Since we never should get system calls from kernel mode we can |
182 |
|
try to optimize for user mode. */ |
183 |
|
|
184 |
|
.text |
185 |
|
.align 4 |
186 |
|
ENTRY(syscall) |
187 |
|
|
188 |
|
pushl $0 |
189 |
|
pushl %eax |
190 |
|
|
191 |
|
/* Push generic and segment registers on stack. */ |
192 |
|
|
193 |
|
pusha |
194 |
|
pushl %ds |
195 |
|
pushl %es |
196 |
|
pushl %fs |
197 |
|
pushl %gs |
198 |
|
cld |
199 |
|
|
200 |
|
/* Set segment registers. */ |
201 |
|
|
202 |
|
mov %ss,%ax |
203 |
|
mov %ax,%ds |
204 |
|
mov %ax,%es |
205 |
|
mov %ax,%gs |
206 |
|
mov %ax,%fs |
207 |
|
|
208 |
|
/* Switch to kernel stack. */ |
209 |
|
|
210 |
|
CPU_NUMBER(%edx) |
211 |
|
movl CX(kernel_stacks,%edx),%ebx |
212 |
|
xchgl %ebx,%esp |
213 |
|
|
214 |
|
|
215 |
|
/* Check so that its a valid system call. */ |
216 |
|
|
217 |
|
movl R_TRAPNO(%ebx), %eax /* trapno in eax */ |
218 |
|
movl EXT(syscalls_count), %edx |
219 |
|
cmp %edx, %eax |
220 |
|
jae .Linvalidsc |
221 |
|
|
222 |
|
|
223 |
|
/* Get system call information (arg cnt). */ |
224 |
|
|
225 |
|
lea EXT(syscalls_table)(, %eax, 8), %esi |
226 |
|
movl 4(%esi), %edx |
227 |
|
testl %edx, %edx |
228 |
|
jz .Lcall_with_no_args |
229 |
|
|
230 |
|
/* Reserve space for arguments on stack. */ |
231 |
|
|
232 |
|
shll $2, %edx /* *= 4 */ |
233 |
|
subl %edx, %esp |
234 |
|
movl %esp, %edi |
235 |
|
|
236 |
|
movl R_UESP(%ebx), %esi /* user stack ptr */ |
237 |
|
addl $4, %esi /* ??? return address */ |
238 |
|
|
239 |
|
/* Copy arguments from user stack to our stack. */ |
240 |
|
|
241 |
|
pushl %ebx /* save frame pointer */ |
242 |
|
pushl %edx |
243 |
|
pushl %esi |
244 |
|
pushl %edi |
245 |
|
call EXT(copyin) |
246 |
|
addl $12, %esp |
247 |
|
popl %ebx |
248 |
|
|
249 |
|
/* Get syscall function address. */ |
250 |
|
|
251 |
|
movl R_TRAPNO(%ebx), %eax |
252 |
|
movl EXT(syscalls_table)(, %eax, 8), %ecx |
253 |
|
|
254 |
|
/* Call function. */ |
255 |
|
|
256 |
|
pushl %ebx |
257 |
|
pushl %edi |
258 |
|
call *%ecx |
259 |
|
.globl __syscall_frame_address_0 |
260 |
|
__syscall_frame_address_0: |
261 |
|
movl 4(%esp), %esp |
262 |
|
jmp _return_from_syscall |
263 |
|
|
264 |
|
/* Fast path for system calls without arguments. */ |
265 |
|
|
266 |
|
.Lcall_with_no_args: |
267 |
|
pushl %ebx |
268 |
|
call *%ecx |
269 |
|
.globl __syscall_frame_address_1 |
270 |
|
__syscall_frame_address_1: |
271 |
|
movl 0(%esp),%esp |
272 |
|
jmp _return_from_syscall /* ??? fall through ? */ |
273 |
|
|
274 |
|
_return_from_syscall: |
275 |
|
popl %gs |
276 |
|
popl %fs |
277 |
|
popl %es |
278 |
|
popl %ds |
279 |
|
popa |
280 |
|
addl $8,%esp |
281 |
|
iret |
282 |
|
|
283 |
|
.Linvalidsc: |
284 |
|
cli |
285 |
|
0: jmp 0b |
286 |
|
|
287 |
|
|
288 |
/* All exceptions enter here with this state: |
/* Trap enter function. |
289 |
|
|
290 |
%esp-> trap number |
On stack we have exception number, error code, old %eip, cold %cs, |
291 |
error code |
old %eflags and old %esp, old %ss if we trapped from user mode. |
292 |
old eip |
|
293 |
old cs |
We save all registers and switch to kernel stack if we trapped from |
294 |
old eflags |
user mode. We call the exception handling function. */ |
295 |
old esp if trapped from user |
|
|
old ss if trapped from user |
|
|
*// |
|
296 |
.text |
.text |
297 |
.align 2 |
.align 2 |
298 |
alltraps: |
alltraps: |
306 |
_trap_set_segs: |
_trap_set_segs: |
307 |
cld |
cld |
308 |
|
|
309 |
/* Trap from kernel mode. |
/* Check if we trapped from user mode. */ |
310 |
No need to switch stacks or load segment registers. */ |
|
311 |
|
testb $3,R_CS(%esp) |
312 |
|
jz _trap_from_kernel |
313 |
|
|
314 |
|
_trap_from_user: |
315 |
|
|
316 |
|
/* Set segment registers. */ |
317 |
|
mov %ss,%ax |
318 |
|
mov %ax,%ds |
319 |
|
mov %ax,%es |
320 |
|
mov %ax,%gs |
321 |
|
mov %ax,%fs |
322 |
|
|
323 |
|
/* Save CR2 register. |
324 |
|
??? should we do this for all traps? */ |
325 |
|
|
326 |
|
movl %cr2, %eax |
327 |
|
movl %eax, R_CR2(%esp) |
328 |
|
|
329 |
|
/* Switch to kernel stack. */ |
330 |
|
|
331 |
|
CPU_NUMBER(%edx) |
332 |
|
movl CX(kernel_stacks,%edx),%ebx |
333 |
|
xchgl %ebx,%esp |
334 |
|
|
335 |
|
/* Call generic exception handler. */ |
336 |
|
|
337 |
|
pushl %ebx |
338 |
|
call cpu_exception_kernel |
339 |
|
movl 0(%esp),%esp |
340 |
|
|
341 |
|
jmp _return_from_kernel |
342 |
|
|
343 |
|
/* Trap from kernel mode. |
344 |
|
No need to switch stacks or load segment registers. */ |
345 |
|
|
346 |
_trap_from_kernel: |
_trap_from_kernel: |
|
#if 1 |
|
347 |
movl %cr2, %eax |
movl %cr2, %eax |
348 |
movl %eax, R_CR2(%esp) |
movl %eax, R_CR2(%esp) |
|
#endif |
|
|
|
|
|
mov %ss,%ax |
|
|
mov %ax,%ds |
|
|
mov %ax,%es |
|
|
mov %ax,%gs |
|
|
mov %ax,%fs |
|
349 |
|
|
350 |
sti |
/* Set segment registers. */ |
351 |
|
mov %ss,%ax |
352 |
|
mov %ax,%ds |
353 |
|
mov %ax,%es |
354 |
|
mov %ax,%gs |
355 |
|
mov %ax,%fs |
356 |
|
|
357 |
|
/* Call generic exception handler. */ |
358 |
|
|
359 |
pushl %esp |
pushl %esp |
360 |
call cpu_exception_kernel |
call cpu_exception_kernel |
361 |
addl $4,%esp |
addl $4,%esp |
362 |
|
|
363 |
|
/* Fall through */ |
364 |
|
|
365 |
|
/* Return from exception. Stack points to exceptin frame. |
366 |
|
??? should this be global ? */ |
367 |
|
|
368 |
.globl _return_from_kernel |
.globl _return_from_kernel |
369 |
_return_from_kernel: |
_return_from_kernel: |
380 |
_kret_iret: |
_kret_iret: |
381 |
iret |
iret |
382 |
|
|
383 |
|
/* cpu_load_context (THREAD) |
384 |
|
|
385 |
|
Load kernel context from THREAD. Store kernel stack in |
386 |
/* Context switch routines for i386. */ |
active_stacks and stack pointer in kernel_stacks. */ |
|
|
|
|
.text |
|
|
.align 4 |
|
|
.globl cpu_load_kstate |
|
|
cpu_load_kstate: |
|
|
movl 4(%esp), %edx |
|
|
|
|
|
movl KSS_ESP(%edx),%esp |
|
|
movl KSS_ESI(%edx),%esi |
|
|
movl KSS_EDI(%edx),%edi |
|
|
movl KSS_EBP(%edx),%ebp |
|
|
movl KSS_EBX(%edx),%ebx |
|
|
xorl %eax,%eax |
|
|
jmp *KSS_EIP(%edx) |
|
|
|
|
|
.align 4 |
|
|
.globl cpu_switch_kstate |
|
|
cpu_switch_kstate: |
|
|
movl 4(%esp), %edx |
|
|
movl %esp, KSS_ESP(%edx) |
|
|
movl %esi, KSS_ESI(%edx) |
|
|
movl %edi, KSS_EDI(%edx) |
|
|
movl %ebp, KSS_EBP(%edx) |
|
|
movl %ebx, KSS_EBX(%edx) |
|
|
popl KSS_EIP(%edx) |
|
|
jmp cpu_load_kstate |
|
|
|
|
|
.bss |
|
|
LEXT(kernel_stacks) |
|
|
.fill 4, NCPUS |
|
|
|
|
|
/* Context switch routines for i386. */ |
|
387 |
|
|
388 |
.text |
.text |
389 |
ENTRY(cpu_load_context) |
ENTRY(cpu_load_context) |
393 |
|
|
394 |
CPU_NUMBER(%eax) |
CPU_NUMBER(%eax) |
395 |
movl %ecx, CX(active_stacks,%eax) |
movl %ecx, CX(active_stacks,%eax) |
396 |
movl %edx, CX(kernel_stack,%eax) |
movl %edx, CX(kernel_stacks,%eax) |
397 |
|
|
398 |
movl KSS_ESP(%edx),%esp |
movl KSS_ESP(%edx),%esp |
399 |
movl KSS_ESI(%edx),%esi |
movl KSS_ESI(%edx),%esi |
403 |
xorl %eax,%eax |
xorl %eax,%eax |
404 |
jmp *KSS_EIP(%edx) |
jmp *KSS_EIP(%edx) |
405 |
|
|
406 |
|
|
407 |
/* This really only has to save registers |
/* This really only has to save registers |
408 |
when there is no explicit continuation. */ |
when there is no explicit continuation. */ |
409 |
|
|
440 |
movl KSS_EBX(%edx),%ebx |
movl KSS_EBX(%edx),%ebx |
441 |
jmp *KSS_EIP(%edx) |
jmp *KSS_EIP(%edx) |
442 |
|
|
443 |
|
|
444 |
|
/* Machine continutation point. We clear the frame pointer |
445 |
|
and call function in %ebx. */ |
446 |
|
|
447 |
ENTRY(cpu_thread_continue) |
ENTRY(cpu_thread_continue) |
448 |
pushl %eax |
pushl %eax |
449 |
xorl %ebp,%ebp |
xorl %ebp,%ebp |
450 |
call *%ebx |
call *%ebx |
451 |
|
|
452 |
|
/* cpu_call_continuation (CONTINUATION) |
453 |
|
|
454 |
|
Reset kernel stack and call CONTINUATION. */ |
455 |
|
|
456 |
ENTRY(cpu_call_continuation) |
ENTRY(cpu_call_continuation) |
457 |
movl 4(%esp),%eax |
movl 4(%esp),%eax |
458 |
movl %esp,%ecx |
movl %esp,%ecx |
462 |
xorl %ebp,%ebp |
xorl %ebp,%ebp |
463 |
jmp *%eax |
jmp *%eax |
464 |
|
|
465 |
/* Copy from user address space. ??? copyin (DST_KA, SRC_UA, COUNTER) */ |
/* Exception return function. Reset kernel stack and return |
466 |
|
to user mode. ??? takes no arguments. */ |
467 |
|
|
468 |
|
.globl EXT(thread_exception_return) |
469 |
|
.globl EXT(thread_bootstrap_return) |
470 |
|
LEXT(thread_exception_return) |
471 |
|
LEXT(thread_bootstrap_return) |
472 |
|
movl %esp,%ecx |
473 |
|
or $(KERNEL_STACK_SIZE-1),%ecx |
474 |
|
movl -3-IKS_SIZE(%ecx),%esp |
475 |
|
jmp _return_from_kernel |
476 |
|
|
477 |
|
|
478 |
|
/* copyin (DST, SRC, COUNT) |
479 |
|
|
480 |
|
Copy COUNT bytes from user address SRC to kernel address DST. |
481 |
|
This do not recover from page faults. Returns zero. */ |
482 |
|
|
483 |
ENTRY(copyin) |
ENTRY(copyin) |
484 |
pushl %esi |
pushl %esi |
512 |
movw %ax, %fs |
movw %ax, %fs |
513 |
|
|
514 |
xorl %eax,%eax /* return 0 for success */ |
xorl %eax,%eax /* return 0 for success */ |
|
copy_ret_1: |
|
515 |
popl %edi /* restore registers */ |
popl %edi /* restore registers */ |
516 |
popl %esi |
popl %esi |
517 |
ret /* and return */ |
ret /* and return */ |
518 |
|
|
|
copy_fail_1: |
|
|
movl $KERNEL_DS, %eax |
|
|
movw %ax, %es |
|
|
movw %ax, %fs |
|
519 |
|
|
520 |
movl $1,%eax /* return 1 for failure */ |
/* copyout (DST, SRC, COUNT) |
521 |
jmp copy_ret_1 /* pop frame and return */ |
|
522 |
|
Copy COUNT bytes from kernel address SRC to user address DST. |
523 |
|
This do not recover from page faults. Returns zero. */ |
524 |
|
|
|
/* Copy to user address space. ??? copyout (DST_VA, SRC_KA, COUNT) */ |
|
525 |
ENTRY(copyout) |
ENTRY(copyout) |
526 |
pushl %esi |
pushl %esi |
527 |
pushl %edi /* save registers */ |
pushl %edi /* save registers */ |
553 |
movw %ax, %fs |
movw %ax, %fs |
554 |
|
|
555 |
xorl %eax,%eax /* return 0 for success */ |
xorl %eax,%eax /* return 0 for success */ |
|
copy_ret_2: |
|
556 |
popl %edi /* restore registers */ |
popl %edi /* restore registers */ |
557 |
popl %esi |
popl %esi |
558 |
ret /* and return */ |
ret /* and return */ |
559 |
|
|
|
copy_fail_2: |
|
|
movl $KERNEL_DS, %eax |
|
|
movw %ax, %es |
|
|
movw %ax, %fs |
|
560 |
|
|
561 |
|
/* SMP locore functions. |
562 |
|
|
563 |
movl $1,%eax /* return 1 for failure */ |
??? these should be moved to its own file. */ |
|
jmp copy_ret_2 /* pop frame and return */ |
|
564 |
|
|
|
|
|
|
/* SMP STUFF! */ |
|
565 |
#if NCPUS > 1 |
#if NCPUS > 1 |
566 |
|
|
567 |
.text |
.text |
633 |
|
|
634 |
#endif /* NCPUS > 1 */ |
#endif /* NCPUS > 1 */ |
635 |
|
|
636 |
|
|
637 |
/* There are three diffrent types of exceptions: |
/* There are three diffrent types of exceptions: |
638 |
|
|
639 |
. traps The CS and EIP values stored when the trap is reported |
. traps The CS and EIP values stored when the trap is reported |
701 |
idttable: |
idttable: |
702 |
|
|
703 |
/* type exception id */ |
/* type exception id */ |
|
#if 1 |
|
704 |
exception (TRAP_DIVIDE_ERROR); |
exception (TRAP_DIVIDE_ERROR); |
705 |
exception (TRAP_DEBUG_EXCEPTION); |
exception (TRAP_DEBUG_EXCEPTION); |
706 |
exception_user (TRAP_BREAKPOINT); |
exception_user (TRAP_BREAKPOINT); |
718 |
exception (TRAP_FPU_ERROR); |
exception (TRAP_FPU_ERROR); |
719 |
exception (TRAP_ALIGNMENT_CHECK); |
exception (TRAP_ALIGNMENT_CHECK); |
720 |
exception (TRAP_MACHINE_CHECK); |
exception (TRAP_MACHINE_CHECK); |
|
#endif |
|
721 |
|
|
722 |
interrupt_master (0); |
interrupt_master (0); |
723 |
interrupt_master (1); |
interrupt_master (1); |
737 |
interrupt_slave (6); |
interrupt_slave (6); |
738 |
interrupt_slave (7); |
interrupt_slave (7); |
739 |
|
|
740 |
|
routine_stub (99, ACC_PL_U|ACC_TRAP_GATE, syscall) |
741 |
|
|
742 |
#if NCPUS > 1 |
#if NCPUS > 1 |
743 |
apic_interrupt (0xfb); |
apic_interrupt (0xfb); |
744 |
apic_interrupt (0xfc); |
apic_interrupt (0xfc); |