FreeBSD/Linux Kernel Cross Reference
sys/i386/locore.s
1 /*
2 * Mach Operating System
3 * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University
4 * Copyright (c) 1991 IBM Corporation
5 * All Rights Reserved.
6 *
7 * Permission to use, copy, modify and distribute this software and its
8 * documentation is hereby granted, provided that both the copyright
9 * notice and this permission notice appear in all copies of the
10 * software, derivative works or modified versions, and any portions
11 * thereof, and that both notices appear in supporting documentation,
12 * and that the nema IBM not be used in advertising or publicity
13 * pertaining to distribution of the software without specific, written
14 * prior permission.
15 *
16 * CARNEGIE MELLON AND IBM ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS"
17 * CONDITION. CARNEGIE MELLON AND IBM DISCLAIM ANY LIABILITY OF ANY KIND FOR
18 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
19 *
20 * Carnegie Mellon requests users of this software to return to
21 *
22 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
23 * School of Computer Science
24 * Carnegie Mellon University
25 * Pittsburgh PA 15213-3890
26 *
27 * any improvements or extensions that they make and grant Carnegie Mellon
28 * the rights to redistribute these changes.
29 */
30
31 /*
32 * HISTORY
33 * $Log: locore.s,v $
34 * Revision 2.25 93/05/10 23:23:41 rvb
35 * Checkin for MK80 branch.
36 * [93/05/10 15:11:52 grm]
37 *
38 * Revision 2.23.1.1 93/03/01 15:22:47 grm
39 * Added TTD teledebug code to setup trampoline upon entry. The
40 * code mirrors ddb.
41 * [93/03/01 grm]
42 *
43 * Revision 2.24 93/05/10 17:46:24 rvb
44 * Use C comments
45 * [93/05/04 17:16:38 rvb]
46 *
47 * Revision 2.23 93/02/04 07:56:32 danner
48 * Added special case of TIME_TRAP_UENTRY for system calls:
49 * TIME_TRAP_SENTRY. It needs to save and restore %eax around the
50 * call to timer_normalize. Fix from OSF Grenoble.
51 * [93/01/20 dbg]
52 * Integrate PS2 code from IBM.
53 * [93/01/18 prithvi]
54 *
55 * Revision 2.22 93/01/28 18:40:56 danner
56 * Make tenmicrosec() generally available. Clean up comment.
57 * [93/01/25 rvb]
58 *
59 * Revision 2.21 92/04/06 01:15:57 rpd
60 * Converted from #-style to /-style comments, for ANSI preprocessors.
61 * [92/04/05 rpd]
62 *
63 * Revision 2.20 92/02/19 16:29:23 elf
64 * Add some MD debugger support.
65 * [92/02/07 rvb]
66 *
67 * Revision 2.19 92/01/03 20:07:57 dbg
68 * Generalize retryable faults. Add inst_fetch to fetch
69 * instruction when segments are in use.
70 * [91/12/06 dbg]
71 *
72 * Remove fixed lower bound for emulated system call table.
73 * [91/10/31 dbg]
74 *
75 * Segment-not-present may also occur during kernel exit sequence.
76 * Call i386_ast_taken to handle delayed floating-point exceptions.
77 * [91/10/29 dbg]
78 *
79 * Revision 2.18 91/10/07 17:24:53 af
80 * tenmicrosec() was all wrong has been expunged, since noone uses
81 * it.
82 * [91/09/04 rvb]
83 *
84 * Revision 2.17 91/08/28 21:39:05 jsb
85 * Add tests for V86 mode in trace and GP fault checks. Clear
86 * direction flag at all kernel entry points.
87 * [91/08/20 dbg]
88 *
89 * Revision 2.16 91/07/31 17:38:38 dbg
90 * Add microsecond timing.
91 *
92 * Save user regs directly in PCB on trap, and switch to separate
93 * kernel stack.
94 *
95 * Make copyin and copyout use longword move if possible.
96 * [91/07/30 16:53:39 dbg]
97 *
98 * Revision 2.15 91/06/19 11:55:16 rvb
99 * cputypes.h->platforms.h
100 * [91/06/12 13:44:59 rvb]
101 *
102 * Revision 2.14 91/05/14 16:11:15 mrt
103 * Correcting copyright
104 *
105 * Revision 2.13 91/05/08 12:39:21 dbg
106 * Put parentheses around substituted immediate expressions, so
107 * that they will pass through the GNU preprocessor.
108 *
109 * Handle multiple CPUS.
110 * [91/04/26 14:36:46 dbg]
111 *
112 * Revision 2.12 91/03/16 14:44:37 rpd
113 * Changed call_continuation to not change spl.
114 * [91/02/17 rpd]
115 * Added call_continuation.
116 * Changed the AST interface.
117 * [91/01/18 rpd]
118 *
119 * Revision 2.11 91/02/05 17:12:59 mrt
120 * Changed to new Mach copyright
121 * [91/02/01 17:35:44 mrt]
122 *
123 * Revision 2.10 91/01/09 22:41:32 rpd
124 * Removed k_user_regs.
125 * [91/01/09 rpd]
126 *
127 * Revision 2.9 91/01/08 17:32:10 rpd
128 * interrupt_returns must check for EFL_VM.
129 * [90/12/21 14:37:44 rvb]
130 *
131 * Add trapv86 for VM thread.
132 * [90/12/19 17:00:56 rvb]
133 *
134 * Revision 2.8 91/01/08 15:10:49 rpd
135 * Replaced thread_bootstrap_user, thread_bootstrap_kernel
136 * with thread_exception_return, thread_syscall_return.
137 * Updated mach_trap_table indexing for new layout.
138 * [90/12/17 rpd]
139 *
140 * Renamed thread_bootstrap to thread_bootstrap_user.
141 * Added thread_bootstrap_kernel.
142 * [90/12/14 rpd]
143 *
144 * Reorganized the pcb.
145 * Added copyinmsg, copyoutmsg synonyms for copyin, copyout.
146 * [90/12/11 rpd]
147 *
148 * Revision 2.7 90/12/20 16:36:21 jeffreyh
149 * Changes for __STDC__
150 * [90/12/07 15:43:29 jeffreyh]
151 *
152 *
153 * Revision 2.6 90/12/04 14:46:11 jsb
154 * iPSC2 -> iPSC386.
155 * [90/12/04 11:17:03 jsb]
156 *
157 * Revision 2.5 90/09/23 17:45:16 jsb
158 * Added support for iPSC386.
159 * [90/09/21 16:40:55 jsb]
160 *
161 * Revision 2.4 90/08/27 21:57:24 dbg
162 * Remove interrupt/trap vectors - get from idt.s.
163 * Fix copyout to check user address on each page boundary.
164 * [90/07/25 dbg]
165 *
166 * Revision 2.3 90/05/21 13:26:44 dbg
167 * Add inl, outl.
168 * [90/05/17 dbg]
169 *
170 * Revision 2.2 90/05/03 15:33:45 dbg
171 * Created.
172 * [90/02/14 dbg]
173 *
174 */
175
176 #include <cpus.h>
177 #include <platforms.h>
178 #include <mach_kdb.h>
179 #include <mach_ttd.h>
180 #include <stat_time.h>
181
182 #include <i386/asm.h>
183 #include <i386/eflags.h>
184 #include <i386/proc_reg.h>
185 #include <i386/trap.h>
186 #include <assym.s>
187
188 #if NCPUS > 1
189
190 #ifdef SYMMETRY
191 #include <sqt/asm_macros.h>
192 #endif
193
194 #define CX(addr,reg) addr(,reg,4)
195
196 #else
197 #define CPU_NUMBER(reg)
198 #define CX(addr,reg) addr
199
200 #endif NCPUS > 1
201
202 /*
203 * Fault recovery.
204 */
205 #define RECOVER_TABLE_START \
206 .text 2 ;\
207 .globl _recover_table ;\
208 _recover_table: ;\
209 .text
210
211 #define RECOVER(addr) \
212 .text 2 ;\
213 .long 9f ;\
214 .long addr ;\
215 .text ;\
216 9:
217
218 #define RECOVER_TABLE_END \
219 .text 2 ;\
220 .globl _recover_table_end ;\
221 _recover_table_end: ;\
222 .text
223
224 /*
225 * Retry table for certain successful faults.
226 */
227 #define RETRY_TABLE_START \
228 .text 3 ;\
229 .globl _retry_table ;\
230 _retry_table: ;\
231 .text
232
233 #define RETRY(addr) \
234 .text 3 ;\
235 .long 9f ;\
236 .long addr ;\
237 .text ;\
238 9:
239
240 #define RETRY_TABLE_END \
241 .text 3 ;\
242 .globl _retry_table_end ;\
243 _retry_table_end: ;\
244 .text
245
246 /*
247 * Allocate recovery and retry tables.
248 */
249 RECOVER_TABLE_START
250 RETRY_TABLE_START
251
252 /*
253 * Timing routines.
254 */
255 #if STAT_TIME
256
257 #define TIME_TRAP_UENTRY
258 #define TIME_TRAP_SENTRY
259 #define TIME_TRAP_UEXIT
260 #define TIME_INT_ENTRY
261 #define TIME_INT_EXIT
262
263 #else /* microsecond timing */
264
265 /*
266 * Microsecond timing.
267 * Assumes a free-running microsecond counter.
268 * no TIMER_MAX check needed.
269 */
270
271 /*
272 * There is only one current time-stamp per CPU, since only
273 * the time-stamp in the current timer is used.
274 * To save time, we allocate the current time-stamps here.
275 */
276 .comm _current_tstamp, 4*NCPUS
277
278 /*
279 * Update time on user trap entry.
280 * 11 instructions (including cli on entry)
281 * Assumes CPU number in %edx.
282 * Uses %eax, %ebx, %ecx.
283 */
284 #define TIME_TRAP_UENTRY \
285 cli /* block interrupts */ ;\
286 movl VA_ETC,%ebx /* get timer value */ ;\
287 movl CX(_current_tstamp,%edx),%ecx /* get old time stamp */;\
288 movl %ebx,CX(_current_tstamp,%edx) /* set new time stamp */;\
289 subl %ecx,%ebx /* elapsed = new-old */ ;\
290 movl CX(_current_timer,%edx),%ecx /* get current timer */ ;\
291 addl %ebx,LOW_BITS(%ecx) /* add to low bits */ ;\
292 jns 0f /* if overflow, */ ;\
293 call timer_normalize /* normalize timer */ ;\
294 0: addl $(TH_SYS_TIMER-TH_USER_TIMER),%ecx ;\
295 /* switch to sys timer */;\
296 movl %ecx,CX(_current_timer,%edx) /* make it current */ ;\
297 sti /* allow interrupts */
298
299 /*
300 * Update time on system call entry.
301 * 11 instructions (including cli on entry)
302 * Assumes CPU number in %edx.
303 * Uses %ebx, %ecx.
304 * Same as TIME_TRAP_UENTRY, but preserves %eax.
305 */
306 #define TIME_TRAP_SENTRY \
307 cli /* block interrupts */ ;\
308 movl VA_ETC,%ebx /* get timer value */ ;\
309 movl CX(_current_tstamp,%edx),%ecx /* get old time stamp */;\
310 movl %ebx,CX(_current_tstamp,%edx) /* set new time stamp */;\
311 subl %ecx,%ebx /* elapsed = new-old */ ;\
312 movl CX(_current_timer,%edx),%ecx /* get current timer */ ;\
313 addl %ebx,LOW_BITS(%ecx) /* add to low bits */ ;\
314 jns 0f /* if overflow, */ ;\
315 pushl %eax /* save %eax */ ;\
316 call timer_normalize /* normalize timer */ ;\
317 popl %eax /* restore %eax */ ;\
318 0: addl $(TH_SYS_TIMER-TH_USER_TIMER),%ecx ;\
319 /* switch to sys timer */;\
320 movl %ecx,CX(_current_timer,%edx) /* make it current */ ;\
321 sti /* allow interrupts */
322
323 /*
324 * update time on user trap exit.
325 * 10 instructions.
326 * Assumes CPU number in %edx.
327 * Uses %ebx, %ecx.
328 */
329 #define TIME_TRAP_UEXIT \
330 cli /* block interrupts */ ;\
331 movl VA_ETC,%ebx /* get timer */ ;\
332 movl CX(_current_tstamp,%edx),%ecx /* get old time stamp */;\
333 movl %ebx,CX(_current_tstamp,%edx) /* set new time stamp */;\
334 subl %ecx,%ebx /* elapsed = new-old */ ;\
335 movl CX(_current_timer,%edx),%ecx /* get current timer */ ;\
336 addl %ebx,LOW_BITS(%ecx) /* add to low bits */ ;\
337 jns 0f /* if overflow, */ ;\
338 call timer_normalize /* normalize timer */ ;\
339 0: addl $(TH_USER_TIMER-TH_SYS_TIMER),%ecx ;\
340 /* switch to user timer */;\
341 movl %ecx,CX(_current_timer,%edx) /* make it current */
342
343 /*
344 * update time on interrupt entry.
345 * 9 instructions.
346 * Assumes CPU number in %edx.
347 * Leaves old timer in %ebx.
348 * Uses %ecx.
349 */
350 #define TIME_INT_ENTRY \
351 movl VA_ETC,%ecx /* get timer */ ;\
352 movl CX(_current_tstamp,%edx),%ebx /* get old time stamp */;\
353 movl %ecx,CX(_current_tstamp,%edx) /* set new time stamp */;\
354 subl %ebx,%ecx /* elapsed = new-old */ ;\
355 movl CX(_current_timer,%edx),%ebx /* get current timer */ ;\
356 addl %ecx,LOW_BITS(%ebx) /* add to low bits */ ;\
357 leal CX(0,%edx),%ecx /* timer is 16 bytes */ ;\
358 lea CX(_kernel_timer,%edx),%ecx /* get interrupt timer*/;\
359 movl %ecx,CX(_current_timer,%edx) /* set timer
360
361 /*
362 * update time on interrupt exit.
363 * 11 instructions
364 * Assumes CPU number in %edx, old timer in %ebx.
365 * Uses %eax, %ecx.
366 */
367 #define TIME_INT_EXIT \
368 movl VA_ETC,%eax /* get timer */ ;\
369 movl CX(_current_tstamp,%edx),%ecx /* get old time stamp */;\
370 movl %eax,CX(_current_tstamp,%edx) /* set new time stamp */;\
371 subl %ecx,%eax /* elapsed = new-old */ ;\
372 movl CX(_current_timer,%edx),%ecx /* get current timer */ ;\
373 addl %eax,LOW_BITS(%ecx) /* add to low bits */ ;\
374 jns 0f /* if overflow, */ ;\
375 call timer_normalize /* normalize timer */ ;\
376 0: testb $0x80,LOW_BITS+3(%ebx) /* old timer overflow? */;\
377 jz 0f /* if overflow, */ ;\
378 movl %ebx,%ecx /* get old timer */ ;\
379 call timer_normalize /* normalize timer */ ;\
380 0: movl %ebx,CX(_current_timer,%edx) /* set timer */
381
382
383 /*
384 * Normalize timer in ecx.
385 * Preserves edx; clobbers eax.
386 */
387 .align 2
388 timer_high_unit:
389 .long TIMER_HIGH_UNIT /* div has no immediate opnd */
390
391 timer_normalize:
392 pushl %edx /* save register */
393 xorl %edx,%edx /* clear divisor high */
394 movl LOW_BITS(%ecx),%eax /* get divisor low */
395 divl timer_high_unit,%eax /* quotient in eax */
396 /* remainder in edx */
397 addl %eax,HIGH_BITS_CHECK(%ecx) /* add high_inc to check */
398 movl %edx,LOW_BITS(%ecx) /* remainder to low_bits */
399 addl %eax,HIGH_BITS(%ecx) /* add high_inc to high bits */
400 popl %edx /* restore register */
401 ret
402
403 /*
404 * Switch to a new timer.
405 */
406 ENTRY(timer_switch)
407 CPU_NUMBER(%edx) /* get this CPU */
408 movl VA_ETC,%ecx /* get timer */
409 movl CX(_current_tstamp,%edx),%eax /* get old time stamp */
410 movl %ecx,CX(_current_tstamp,%edx) /* set new time stamp */
411 subl %ecx,%eax /* elapsed = new - old */
412 movl CX(_current_timer,%edx),%ecx /* get current timer */
413 addl %eax,LOW_BITS(%ecx) /* add to low bits */
414 jns 0f /* if overflow, */
415 call timer_normalize /* normalize timer */
416 0:
417 movl S_ARG0,%ecx /* get new timer */
418 movl %ecx,CX(_current_timer,%edx) /* set timer */
419 ret
420
421 /*
422 * Initialize the first timer for a CPU.
423 */
424 ENTRY(start_timer)
425 CPU_NUMBER(%edx) /* get this CPU */
426 movl VA_ETC,%ecx /* get timer */
427 movl %ecx,CX(_current_tstamp,%edx) /* set initial time stamp */
428 movl S_ARG0,%ecx /* get timer */
429 movl %ecx,CX(_current_timer,%edx) /* set initial timer */
430 ret
431
432 #endif /* accurate timing */
433
434 /**/
435
436 /*
437 * Trap/interrupt entry points.
438 *
439 * All traps must create the following save area on the kernel stack:
440 *
441 * gs
442 * fs
443 * es
444 * ds
445 * edi
446 * esi
447 * ebp
448 * cr2 if page fault - otherwise unused
449 * ebx
450 * edx
451 * ecx
452 * eax
453 * trap number
454 * error code
455 * eip
456 * cs
457 * eflags
458 * user esp - if from user
459 * user ss - if from user
460 * es - if from V86 thread
461 * ds - if from V86 thread
462 * fs - if from V86 thread
463 * gs - if from V86 thread
464 *
465 */
466
467 /*
468 * General protection or segment-not-present fault.
469 * Check for a GP/NP fault in the kernel_return
470 * sequence; if there, report it as a GP/NP fault on the user's instruction.
471 *
472 * esp-> 0: trap code (NP or GP)
473 * 4: segment number in error
474 * 8 eip
475 * 12 cs
476 * 16 eflags
477 * 20 old registers (trap is from kernel)
478 */
479 ENTRY(t_gen_prot)
480 pushl $(T_GENERAL_PROTECTION) /* indicate fault type */
481 jmp trap_check_kernel_exit /* check for kernel exit sequence */
482
483 ENTRY(t_segnp)
484 pushl $(T_SEGMENT_NOT_PRESENT)
485 /* indicate fault type */
486
487 trap_check_kernel_exit:
488 testl $(EFL_VM),16(%esp) /* is trap from V86 mode? */
489 jnz _alltraps /* isn`t kernel trap if so */
490 testl $3,12(%esp) /* is trap from kernel mode? */
491 jne _alltraps /* if so: */
492 /* check for the kernel exit sequence */
493 cmpl $_kret_iret,8(%esp) /* on IRET? */
494 je fault_iret
495 cmpl $_kret_popl_ds,8(%esp) /* popping DS? */
496 je fault_popl_ds
497 cmpl $_kret_popl_es,8(%esp) /* popping ES? */
498 je fault_popl_es
499 cmpl $_kret_popl_fs,8(%esp) /* popping FS? */
500 je fault_popl_fs
501 cmpl $_kret_popl_gs,8(%esp) /* popping GS? */
502 je fault_popl_gs
503 take_fault: /* if none of the above: */
504 jmp _alltraps /* treat as normal trap. */
505
506 /*
507 * GP/NP fault on IRET: CS or SS is in error.
508 * All registers contain the user's values.
509 *
510 * on SP is
511 * 0 trap number
512 * 4 errcode
513 * 8 eip
514 * 12 cs --> trapno
515 * 16 efl --> errcode
516 * 20 user eip
517 * 24 user cs
518 * 28 user eflags
519 * 32 user esp
520 * 36 user ss
521 */
522 fault_iret:
523 movl %eax,8(%esp) /* save eax (we don`t need saved eip) */
524 popl %eax /* get trap number */
525 movl %eax,12-4(%esp) /* put in user trap number */
526 popl %eax /* get error code */
527 movl %eax,16-8(%esp) /* put in user errcode */
528 popl %eax /* restore eax */
529 jmp _alltraps /* take fault */
530
531 /*
532 * Fault restoring a segment register. The user's registers are still
533 * saved on the stack. The offending segment register has not been
534 * popped.
535 */
536 fault_popl_ds:
537 popl %eax /* get trap number */
538 popl %edx /* get error code */
539 addl $12,%esp /* pop stack to user regs */
540 jmp push_es /* (DS on top of stack) */
541 fault_popl_es:
542 popl %eax /* get trap number */
543 popl %edx /* get error code */
544 addl $12,%esp /* pop stack to user regs */
545 jmp push_fs /* (ES on top of stack) */
546 fault_popl_fs:
547 popl %eax /* get trap number */
548 popl %edx /* get error code */
549 addl $12,%esp /* pop stack to user regs */
550 jmp push_gs /* (FS on top of stack) */
551 fault_popl_gs:
552 popl %eax /* get trap number */
553 popl %edx /* get error code */
554 addl $12,%esp /* pop stack to user regs */
555 jmp push_segregs /* (GS on top of stack) */
556
557 push_es:
558 pushl %es /* restore es, */
559 push_fs:
560 pushl %fs /* restore fs, */
561 push_gs:
562 pushl %gs /* restore gs. */
563 push_segregs:
564 movl %eax,R_TRAPNO(%esp) /* set trap number */
565 movl %edx,R_ERR(%esp) /* set error code */
566 jmp trap_set_segs /* take trap */
567
568 /*
569 * Debug trap. Check for single-stepping across system call into
570 * kernel. If this is the case, taking the debug trap has turned
571 * off single-stepping - save the flags register with the trace
572 * bit set.
573 */
574 ENTRY(t_debug)
575 testl $(EFL_VM),8(%esp) /* is trap from V86 mode? */
576 jnz 0f /* isn`t kernel trap if so */
577 testl $3,4(%esp) /* is trap from kernel mode? */
578 jnz 0f /* if so: */
579 cmpl $syscall_entry,(%esp) /* system call entry? */
580 jne 0f /* if so: */
581 /* flags are sitting where syscall */
582 /* wants them */
583 addl $8,%esp /* remove eip/cs */
584 jmp syscall_entry_2 /* continue system call entry */
585
586 0: pushl $0 /* otherwise: */
587 pushl $(T_DEBUG) /* handle as normal */
588 jmp _alltraps /* debug fault */
589
590 /*
591 * Page fault traps save cr2.
592 */
593 ENTRY(t_page_fault)
594 pushl $(T_PAGE_FAULT) /* mark a page fault trap */
595 pusha /* save the general registers */
596 movl %cr2,%eax /* get the faulting address */
597 movl %eax,12(%esp) /* save in esp save slot */
598 jmp trap_push_segs /* continue fault */
599
600 /*
601 * All 'exceptions' enter here with:
602 * esp-> trap number
603 * error code
604 * old eip
605 * old cs
606 * old eflags
607 * old esp if trapped from user
608 * old ss if trapped from user
609 */
610 ENTRY(alltraps)
611 pusha /* save the general registers */
612 trap_push_segs:
613 pushl %ds /* and the segment registers */
614 pushl %es
615 pushl %fs
616 pushl %gs
617
618 trap_set_segs:
619 cld /* clear direction flag */
620 testl $(EFL_VM),R_EFLAGS(%esp) /* in V86 mode? */
621 jnz trap_from_user /* user mode trap if so */
622 testb $3,R_CS(%esp) /* user mode trap? */
623 jz trap_from_kernel /* kernel trap if not */
624 trap_from_user:
625 mov %ss,%ax /* switch to kernel data segment */
626 mov %ax,%ds /* (same as kernel stack segment) */
627 mov %ax,%es
628
629 CPU_NUMBER(%edx)
630 TIME_TRAP_UENTRY
631
632 movl CX(_kernel_stack,%edx),%ebx
633 xchgl %ebx,%esp /* switch to kernel stack */
634 /* user regs pointer already set */
635 _take_trap:
636 pushl %ebx /* pass register save area to trap */
637 call _user_trap /* call user trap routine */
638 movl 4(%esp),%esp /* switch back to PCB stack */
639
640 /*
641 * Return from trap or system call, checking for ASTs.
642 * On PCB stack.
643 */
644
645 _return_from_trap:
646 CPU_NUMBER(%edx)
647 cmpl $0,CX(_need_ast,%edx)
648 jz _return_to_user /* if we need an AST: */
649
650 movl CX(_kernel_stack,%edx),%esp
651 /* switch to kernel stack */
652 call _i386_astintr /* take the AST */
653 popl %esp /* switch back to PCB stack */
654 jmp _return_from_trap /* and check again (rare) */
655 /* ASTs after this point will */
656 /* have to wait */
657
658 _return_to_user:
659 TIME_TRAP_UEXIT
660
661 /*
662 * Return from kernel mode to interrupted thread.
663 */
664
665 _return_from_kernel:
666 _kret_popl_gs:
667 popl %gs /* restore segment registers */
668 _kret_popl_fs:
669 popl %fs
670 _kret_popl_es:
671 popl %es
672 _kret_popl_ds:
673 popl %ds
674 popa /* restore general registers */
675 addl $8,%esp /* discard trap number and error code */
676 _kret_iret:
677 iret /* return from interrupt */
678
679
680 /*
681 * Trap from kernel mode. No need to switch stacks or load segment registers.
682 */
683 trap_from_kernel:
684 #if MACH_KDB || MACH_TTD
685 movl %ss,%ax
686 movl %ax,%ds
687 movl %ax,%es /* switch to kernel data seg */
688 movl %esp,%ebx /* save current stack */
689
690 cmpl _int_stack_high,%esp /* on an interrupt stack? */
691 jb 1f /* OK if so */
692
693 CPU_NUMBER(%edx) /* get CPU number */
694 cmpl CX(_kernel_stack,%edx),%esp
695 /* already on kernel stack? */
696 ja 0f
697 cmpl CX(_active_stacks,%edx),%esp
698 ja 1f /* switch if not */
699 0:
700 movl CX(_kernel_stack,%edx),%esp
701 1:
702 pushl %ebx /* save old stack */
703 pushl %ebx /* pass as parameter */
704 call _kernel_trap /* to kernel trap routine */
705 addl $4,%esp /* pop parameter */
706 popl %esp /* return to old stack */
707 #else /* MACH_KDB || MACH_TTD */
708 pushl %esp /* pass parameter */
709 call _kernel_trap /* to kernel trap routine */
710 addl $4,%esp /* pop parameter */
711 #endif /* MACH_KDB || MACH_TTD */
712 jmp _return_from_kernel
713
714
715 /*
716 * Called as a function, makes the current thread
717 * return from the kernel as if from an exception.
718 */
719
720 .globl _thread_exception_return
721 .globl _thread_bootstrap_return
722 _thread_exception_return:
723 _thread_bootstrap_return:
724 movl %esp,%ecx /* get kernel stack */
725 or $(KERNEL_STACK_SIZE-1),%ecx
726 movl -3-IKS_SIZE(%ecx),%esp /* switch back to PCB stack */
727 jmp _return_from_trap
728
729 /*
730 * Called as a function, makes the current thread
731 * return from the kernel as if from a syscall.
732 * Takes the syscall's return code as an argument.
733 */
734
735 ENTRY(thread_syscall_return)
736 movl S_ARG0,%eax /* get return value */
737 movl %esp,%ecx /* get kernel stack */
738 or $(KERNEL_STACK_SIZE-1),%ecx
739 movl -3-IKS_SIZE(%ecx),%esp /* switch back to PCB stack */
740 movl %eax,R_EAX(%esp) /* save return value */
741 jmp _return_from_trap
742
743 ENTRY(call_continuation)
744 movl S_ARG0,%eax /* get continuation */
745 movl %esp,%ecx /* get kernel stack */
746 or $(KERNEL_STACK_SIZE-1),%ecx
747 addl $(-3-IKS_SIZE),%ecx
748 movl %ecx,%esp /* pop the stack */
749 xorl %ebp,%ebp /* zero frame pointer */
750 jmp *%eax /* goto continuation */
751
752 /*
753 * All interrupts enter here.
754 * old %eax on stack; interrupt number in %eax.
755 */
756 ENTRY(all_intrs)
757 pushl %ecx /* save registers */
758 pushl %edx
759 cld /* clear direction flag */
760
761 cmpl %ss:_int_stack_high,%esp /* on an interrupt stack? */
762 jb int_from_intstack /* if not: */
763
764 pushl %ds /* save segment registers */
765 pushl %es
766 mov %ss,%dx /* switch to kernel segments */
767 mov %dx,%ds
768 mov %dx,%es
769
770 CPU_NUMBER(%edx)
771
772 movl CX(_int_stack_top,%edx),%ecx
773 xchgl %ecx,%esp /* switch to interrupt stack */
774
775 #if STAT_TIME
776 pushl %ecx /* save pointer to old stack */
777 #else
778 pushl %ebx /* save %ebx - out of the way */
779 /* so stack looks the same */
780 pushl %ecx /* save pointer to old stack */
781 TIME_INT_ENTRY /* do timing */
782 #endif
783
784 call _interrupt /* call generic interrupt routine */
785
786 .globl _return_to_iret
787 _return_to_iret: /* ( label for kdb_kintr and hardclock) */
788
789 CPU_NUMBER(%edx)
790 #if STAT_TIME
791 #else
792 TIME_INT_EXIT /* do timing */
793 movl 4(%esp),%ebx /* restore the extra reg we saved */
794 #endif
795
796 popl %esp /* switch back to old stack */
797
798 testl $(EFL_VM),I_EFL(%esp) /* if in V86 */
799 jnz 0f /* or */
800 testb $3,I_CS(%esp) /* user mode, */
801 jz 1f /* check for ASTs */
802 0:
803 cmpl $0,CX(_need_ast,%edx)
804 jnz ast_from_interrupt /* take it if so */
805 1:
806 pop %es /* restore segment regs */
807 pop %ds
808 pop %edx
809 pop %ecx
810 pop %eax
811 iret /* return to caller */
812
813 int_from_intstack:
814 call _interrupt /* call interrupt routine */
815 _return_to_iret_i: /* ( label for kdb_kintr) */
816 pop %edx /* must have been on kernel segs */
817 pop %ecx
818 pop %eax /* no ASTs */
819 iret
820
821 /*
822 * Take an AST from an interrupt.
823 * On PCB stack.
824 * sp-> es -> edx
825 * ds -> ecx
826 * edx -> eax
827 * ecx -> trapno
828 * eax -> code
829 * eip
830 * cs
831 * efl
832 * esp
833 * ss
834 */
835 ast_from_interrupt:
836 pop %es /* restore all registers ... */
837 pop %ds
838 popl %edx
839 popl %ecx
840 popl %eax
841 pushl $0 /* zero code */
842 pushl $0 /* zero trap number */
843 pusha /* save general registers */
844 push %ds /* save segment registers */
845 push %es
846 push %fs
847 push %gs
848 mov %ss,%dx /* switch to kernel segments */
849 mov %dx,%ds
850 mov %dx,%es
851
852 CPU_NUMBER(%edx)
853 TIME_TRAP_UENTRY
854
855 movl CX(_kernel_stack,%edx),%esp
856 /* switch to kernel stack */
857 call _i386_astintr /* take the AST */
858 popl %esp /* back to PCB stack */
859 jmp _return_from_trap /* return */
860
861 #if MACH_KDB
862 /*
863 * kdb_kintr: enter kdb from keyboard interrupt.
864 * Chase down the stack frames until we find one whose return
865 * address is the interrupt handler. At that point, we have:
866 *
867 * frame-> saved %ebp
868 * return address in interrupt handler
869 * iunit
870 * [ PS2 - saved interrupt number ]
871 * saved SPL
872 * return address == return_to_iret_i
873 * saved %edx
874 * saved %ecx
875 * saved %eax
876 * saved %eip
877 * saved %cs
878 * saved %efl
879 *
880 * OR:
881 * frame-> saved %ebp
882 * return address in interrupt handler
883 * iunit
884 * [ PS2 - saved interrupt number ]
885 * saved SPL
886 * return address == return_to_iret
887 * pointer to save area on old stack
888 * [ saved %ebx, if accurate timing ]
889 *
890 * old stack: saved %es
891 * saved %ds
892 * saved %edx
893 * saved %ecx
894 * saved %eax
895 * saved %eip
896 * saved %cs
897 * saved %efl
898 *
899 * Call kdb, passing it that register save area.
900 */
901
902 #ifdef PS2
903 #define RET_OFFSET 20
904 #else /* not PS2 */
905 #define RET_OFFSET 16
906 #endif /* PS2 */
907
908 ENTRY(kdb_kintr)
909 movl %ebp,%eax /* save caller`s frame pointer */
910 movl $_return_to_iret,%ecx /* interrupt return address 1 */
911 movl $_return_to_iret_i,%edx /* interrupt return address 2 */
912
913 0: cmpl RET_OFFSET(%eax),%ecx /* does this frame return to */
914 /* interrupt handler (1)? */
915 je 1f
916 cmpl RET_OFFSET(%eax),%edx /* interrupt handler (2)? */
917 je 2f /* if not: */
918 movl (%eax),%eax /* try next frame */
919 jmp 0b
920
921 1: movl $kdb_from_iret,RET_OFFSET(%eax)
922 ret /* returns to kernel/user stack */
923
924 2: movl $kdb_from_iret_i,RET_OFFSET(%eax)
925 /* returns to interrupt stack */
926 ret
927
928 /*
929 * On return from keyboard interrupt, we will execute
930 * kdb_from_iret_i
931 * if returning to an interrupt on the interrupt stack
932 * kdb_from_iret
933 * if returning to an interrupt on the user or kernel stack
934 */
935 kdb_from_iret:
936 /* save regs in known locations */
937 #if STAT_TIME
938 pushl %ebx /* caller`s %ebx is in reg */
939 #else
940 movl 4(%esp),%eax /* get caller`s %ebx */
941 pushl %eax /* push on stack */
942 #endif
943 pushl %ebp
944 pushl %esi
945 pushl %edi
946 push %fs
947 push %gs
948 pushl %esp /* pass regs */
949 call _kdb_kentry /* to kdb */
950 addl $4,%esp /* pop parameters */
951 pop %gs /* restore registers */
952 pop %fs
953 popl %edi
954 popl %esi
955 popl %ebp
956 #if STAT_TIME
957 popl %ebx
958 #else
959 popl %eax
960 movl %eax,4(%esp)
961 #endif
962 jmp _return_to_iret /* normal interrupt return */
963
964 kdb_from_iret_i: /* on interrupt stack */
965 pop %edx /* restore saved registers */
966 pop %ecx
967 pop %eax
968 pushl $0 /* zero error code */
969 pushl $0 /* zero trap number */
970 pusha /* save general registers */
971 push %ds /* save segment registers */
972 push %es
973 push %fs
974 push %gs
975 pushl %esp /* pass regs, */
976 pushl $0 /* code, */
977 pushl $-1 /* type to kdb */
978 call _kdb_trap
979 addl $12,%esp /* remove parameters */
980 pop %gs /* restore segment registers */
981 pop %fs
982 pop %es
983 pop %ds
984 popa /* restore general registers */
985 addl $8,%esp
986 iret
987
988 #endif MACH_KDB
989
990 #if MACH_TTD
991 /*
992 * Same code as that above for the keyboard entry into kdb.
993 */
994 ENTRY(kttd_intr)
995 movl %ebp,%eax /* save caller`s frame pointer */
996 movl $_return_to_iret,%ecx /* interrupt return address 1 */
997 movl $_return_to_iret_i,%edx /* interrupt return address 2 */
998
999 0: cmpl 16(%eax),%ecx /* does this frame return to */
1000 /* interrupt handler (1)? */
1001 je 1f
1002 cmpl 16(%eax),%edx /* interrupt handler (2)? */
1003 je 2f /* if not: */
1004 movl (%eax),%eax /* try next frame */
1005 jmp 0b
1006
1007 1: movl $ttd_from_iret,16(%eax) /* returns to kernel/user stack */
1008 ret
1009
1010 2: movl $ttd_from_iret_i,16(%eax)
1011 /* returns to interrupt stack */
1012 ret
1013
1014 /*
1015 * On return from keyboard interrupt, we will execute
1016 * ttd_from_iret_i
1017 * if returning to an interrupt on the interrupt stack
1018 * ttd_from_iret
1019 * if returning to an interrupt on the user or kernel stack
1020 */
1021 ttd_from_iret:
1022 /* save regs in known locations */
1023 #if STAT_TIME
1024 pushl %ebx /* caller`s %ebx is in reg */
1025 #else
1026 movl 4(%esp),%eax /* get caller`s %ebx */
1027 pushl %eax /* push on stack */
1028 #endif
1029 pushl %ebp
1030 pushl %esi
1031 pushl %edi
1032 push %fs
1033 push %gs
1034 pushl %esp /* pass regs */
1035 call _kttd_netentry /* to kdb */
1036 addl $4,%esp /* pop parameters */
1037 pop %gs /* restore registers */
1038 pop %fs
1039 popl %edi
1040 popl %esi
1041 popl %ebp
1042 #if STAT_TIME
1043 popl %ebx
1044 #else
1045 popl %eax
1046 movl %eax,4(%esp)
1047 #endif
1048 jmp _return_to_iret /* normal interrupt return */
1049
1050 ttd_from_iret_i: /* on interrupt stack */
1051 pop %edx /* restore saved registers */
1052 pop %ecx
1053 pop %eax
1054 pushl $0 /* zero error code */
1055 pushl $0 /* zero trap number */
1056 pusha /* save general registers */
1057 push %ds /* save segment registers */
1058 push %es
1059 push %fs
1060 push %gs
1061 pushl %esp /* pass regs, */
1062 pushl $0 /* code, */
1063 pushl $-1 /* type to kdb */
1064 call _kttd_trap
1065 addl $12,%esp /* remove parameters */
1066 pop %gs /* restore segment registers */
1067 pop %fs
1068 pop %es
1069 pop %ds
1070 popa /* restore general registers */
1071 addl $8,%esp
1072 iret
1073
1074 #endif /* MACH_TTD */
1075
1076 /*
1077 * System call enters through a call gate. Flags are not saved -
1078 * we must shuffle stack to look like trap save area.
1079 *
1080 * esp-> old eip
1081 * old cs
1082 * old esp
1083 * old ss
1084 *
1085 * eax contains system call number.
1086 */
1087 ENTRY(syscall)
1088 syscall_entry:
1089 pushf /* save flags as soon as possible */
1090 syscall_entry_2:
1091 pushl %eax /* save system call number */
1092 pushl $0 /* clear trap number slot */
1093
1094 pusha /* save the general registers */
1095 pushl %ds /* and the segment registers */
1096 pushl %es
1097 pushl %fs
1098 pushl %gs
1099
1100 mov %ss,%dx /* switch to kernel data segment */
1101 mov %dx,%ds
1102 mov %dx,%es
1103
1104 /*
1105 * Shuffle eflags,eip,cs into proper places
1106 */
1107
1108 movl R_EIP(%esp),%ebx /* eflags are in EIP slot */
1109 movl R_CS(%esp),%ecx /* eip is in CS slot */
1110 movl R_EFLAGS(%esp),%edx /* cs is in EFLAGS slot */
1111 movl %ecx,R_EIP(%esp) /* fix eip */
1112 movl %edx,R_CS(%esp) /* fix cs */
1113 movl %ebx,R_EFLAGS(%esp) /* fix eflags */
1114
1115 CPU_NUMBER(%edx)
1116 TIME_TRAP_SENTRY
1117
1118 movl CX(_kernel_stack,%edx),%ebx
1119 /* get current kernel stack */
1120 xchgl %ebx,%esp /* switch stacks - %ebx points to */
1121 /* user registers. */
1122 /* user regs pointer already set */
1123
1124 /*
1125 * Check for MACH or emulated system call
1126 */
1127
1128 movl CX(_active_threads,%edx),%edx
1129 /* point to current thread */
1130 movl TH_TASK(%edx),%edx /* point to task */
1131 movl TASK_EMUL(%edx),%edx /* get emulation vector */
1132 orl %edx,%edx /* if none, */
1133 je syscall_native /* do native system call */
1134 movl %eax,%ecx /* copy system call number */
1135 subl DISP_MIN(%edx),%ecx /* get displacement into syscall */
1136 /* vector table */
1137 jl syscall_native /* too low - native system call */
1138 cmpl DISP_COUNT(%edx),%ecx /* check range */
1139 jnl syscall_native /* too high - native system call */
1140 movl DISP_VECTOR(%edx,%ecx,4),%edx
1141 /* get the emulation vector */
1142 orl %edx,%edx /* emulated system call if not zero */
1143 jnz syscall_emul
1144
1145 /*
1146 * Native system call.
1147 */
1148 syscall_native:
1149 negl %eax /* get system call number */
1150 jl mach_call_range /* out of range if it was positive */
1151 cmpl _mach_trap_count,%eax /* check system call table bounds */
1152 jg mach_call_range /* error if out of range */
1153 shll $4,%eax /* manual indexing */
1154 movl _mach_trap_table(%eax),%ecx
1155 /* get number of arguments */
1156 jecxz mach_call_call /* skip argument copy if none */
1157
1158 movl R_UESP(%ebx),%esi /* get user stack pointer */
1159 lea 4(%esi,%ecx,4),%esi /* skip user return address, */
1160 /* and point past last argument */
1161 cmpl $(VM_MAX_ADDRESS),%esi /* in user space? */
1162 ja mach_call_addr /* address error if not */
1163 movl %esp,%edx /* save kernel ESP for error recovery */
1164
1165 0: subl $4,%esi
1166 RECOVER(mach_call_addr_push)
1167 pushl (%esi) /* push argument on stack */
1168 loop 0b /* loop for all arguments */
1169
1170 mach_call_call:
1171 call *_mach_trap_table+4(%eax)
1172 /* call procedure */
1173 movl %esp,%ecx /* get kernel stack */
1174 or $(KERNEL_STACK_SIZE-1),%ecx
1175 movl -3-IKS_SIZE(%ecx),%esp /* switch back to PCB stack */
1176 movl %eax,R_EAX(%esp) /* save return value */
1177 jmp _return_from_trap /* return to user */
1178
1179 /*
1180 * Address out of range. Change to page fault.
1181 * %esi holds failing address.
1182 */
1183 mach_call_addr_push:
1184 movl %edx,%esp /* clean parameters from stack */
1185 mach_call_addr:
1186 movl %esi,R_CR2(%ebx) /* set fault address */
1187 movl $(T_PAGE_FAULT),R_TRAPNO(%ebx)
1188 /* set page-fault trap */
1189 movl $(T_PF_USER),R_ERR(%ebx)
1190 /* set error code - read user space */
1191 jmp _take_trap /* treat as a trap */
1192
1193 /*
1194 * System call out of range. Treat as invalid-instruction trap.
1195 * (? general protection?)
1196 */
1197 mach_call_range:
1198 movl $(T_INVALID_OPCODE),R_TRAPNO(%ebx)
1199 /* set invalid-operation trap */
1200 movl $0,R_ERR(%ebx) /* clear error code */
1201 jmp _take_trap /* treat as a trap */
1202
1203 /*
1204 * User space emulation of system calls.
1205 * edx - user address to handle syscall
1206 *
1207 * User stack will become:
1208 * uesp-> eflags
1209 * eip
1210 * eax still contains syscall number.
1211 */
1212 syscall_emul:
1213 movl R_UESP(%ebx),%edi /* get user stack pointer */
1214 cmpl $(VM_MAX_ADDRESS),%edi /* in user space? */
1215 ja syscall_addr /* address error if not */
1216 subl $8,%edi /* push space for new arguments */
1217 cmpl $(VM_MIN_ADDRESS),%edi /* still in user space? */
1218 jb syscall_addr /* error if not */
1219 movl R_EFLAGS(%ebx),%eax /* move flags */
1220 RECOVER(syscall_addr)
1221 movl %eax,0(%edi) /* to user stack */
1222 movl R_EIP(%ebx),%eax /* move eip */
1223 RECOVER(syscall_addr)
1224 movl %eax,4(%edi) /* to user stack */
1225 movl %edi,R_UESP(%ebx) /* set new user stack pointer */
1226 movl %edx,R_EIP(%ebx) /* change return address to trap */
1227 movl %ebx,%esp /* back to PCB stack */
1228 jmp _return_from_trap /* return to user */
1229
1230 /*
1231 * Address error - address is in %edi.
1232 */
1233 syscall_addr:
1234 movl %edi,R_CR2(%ebx) /* set fault address */
1235 movl $(T_PAGE_FAULT),R_TRAPNO(%ebx)
1236 /* set page-fault trap */
1237 movl $(T_PF_USER),R_ERR(%ebx)
1238 /* set error code - read user space */
1239 jmp _take_trap /* treat as a trap */
1240
1241 /**/
1242 /*
1243 * Utility routines.
1244 */
1245
1246 /*
1247 * Copy from user address space.
1248 * arg0: user address
1249 * arg1: kernel address
1250 * arg2: byte count
1251 */
1252 ENTRY(copyin)
1253 Entry(copyinmsg)
1254 pushl %esi
1255 pushl %edi /* save registers */
1256
1257 movl 8+S_ARG0,%esi /* get user start address */
1258 movl 8+S_ARG1,%edi /* get kernel destination address */
1259 movl 8+S_ARG2,%edx /* get count */
1260
1261 #if (VM_MIN_ADDRESS != 0)
1262 cmpl $(VM_MIN_ADDRESS),%esi /* is start within user space? */
1263 jb copy_fail
1264 #endif
1265 lea 0(%esi,%edx),%eax /* get user end address + 1 */
1266 cmpl %esi,%eax
1267 jb copy_fail /* fail if wrap-around */
1268 cmpl $(VM_MAX_ADDRESS),%eax
1269 ja copy_fail /* or above user range */
1270
1271 cld /* count up */
1272 movl %edx,%ecx /* move by longwords first */
1273 shrl $2,%ecx
1274 RECOVER(copy_fail)
1275 rep
1276 movsl /* move longwords */
1277 movl %edx,%ecx /* now move remaining bytes */
1278 andl $3,%ecx
1279 RECOVER(copy_fail)
1280 rep
1281 movsb
1282 xorl %eax,%eax /* return 0 for success */
1283 copy_ret:
1284 popl %edi /* restore registers */
1285 popl %esi
1286 ret /* and return */
1287
1288 copy_fail:
1289 movl $1,%eax /* return 1 for failure */
1290 jmp copy_ret /* pop frame and return */
1291
1292 /*
1293 * Copy to user address space.
1294 * arg0: kernel address
1295 * arg1: user address
1296 * arg2: byte count
1297 */
1298 ENTRY(copyout)
1299 Entry(copyoutmsg)
1300 pushl %esi
1301 pushl %edi /* save registers */
1302
1303 movl 8+S_ARG0,%esi /* get kernel start address */
1304 movl 8+S_ARG1,%edi /* get user start address */
1305 movl 8+S_ARG2,%edx /* get count */
1306
1307 #if (VM_MIN_ADDRESS != 0)
1308 cmpl $(VM_MIN_ADDRESS),%edi /* within user space? */
1309 jb copy_fail
1310 #endif
1311 leal 0(%edi,%edx),%eax /* get user end address + 1 */
1312 cmpl %edi,%eax
1313 jb copy_fail /* fail if wrap-around */
1314 cmpl $(VM_MAX_ADDRESS),%eax
1315 ja copy_fail /* or above user range */
1316
1317 /*
1318 * Check whether user address space is writable
1319 * before writing to it - hardware is broken.
1320 */
1321 copyout_retry:
1322 movl %cr3,%ecx /* point to page directory */
1323 movl %edi,%eax /* get page directory bits */
1324 shrl $(PDESHIFT),%eax /* from user address */
1325 movl KERNELBASE(%ecx,%eax,4),%ecx
1326 /* get page directory pointer */
1327 testl $(PTE_V),%ecx /* present? */
1328 jz 0f /* if not, fault is OK */
1329 andl $(PTE_PFN),%ecx /* isolate page frame address */
1330 movl %edi,%eax /* get page table bits */
1331 shrl $(PTESHIFT),%eax
1332 andl $(PTEMASK),%eax /* from user address */
1333 leal KERNELBASE(%ecx,%eax,4),%ecx
1334 /* point to page table entry */
1335 movl (%ecx),%eax /* get it */
1336 testl $(PTE_V),%eax /* present? */
1337 jz 0f /* if not, fault is OK */
1338 testl $(PTE_W),%eax /* writable? */
1339 jnz 0f /* OK if so */
1340 /*
1341 * Not writable - must fake a fault. Turn off access to the page.
1342 */
1343 andl $(PTE_INVALID),(%ecx) /* turn off valid bit */
1344 movl %cr3,%eax /* invalidate TLB */
1345 movl %eax,%cr3
1346 0:
1347 /*
1348 * Copy only what fits on the current destination page.
1349 * Check for write-fault again on the next page.
1350 */
1351 leal NBPG(%edi),%eax /* point to */
1352 andl $(-NBPG),%eax /* start of next page */
1353 subl %edi,%eax /* get number of bytes to that point */
1354 cmpl %edx,%eax /* bigger than count? */
1355 jle 1f /* if so, */
1356 movl %edx,%eax /* use count */
1357 1:
1358 cld /* count up */
1359 movl %eax,%ecx /* move by longwords first */
1360 shrl $2,%ecx
1361 RECOVER(copy_fail)
1362 RETRY(copyout_retry)
1363 rep
1364 movsl
1365 movl %eax,%ecx /* now move remaining bytes */
1366 andl $3,%ecx
1367 RECOVER(copy_fail)
1368 RETRY(copyout_retry)
1369 rep
1370 movsb /* move */
1371 subl %eax,%edx /* decrement count */
1372 jg copyout_retry /* restart on next page if not done */
1373 xorl %eax,%eax /* return 0 for success */
1374 popl %edi /* restore registers */
1375 popl %esi
1376 ret /* and return */
1377
1378 /*
1379 * FPU routines.
1380 */
1381
1382 /*
1383 * Initialize FPU.
1384 */
1385 ENTRY(_fninit)
1386 fninit
1387 ret
1388
1389 /*
1390 * Read control word
1391 */
1392 ENTRY(_fstcw)
1393 pushl %eax /* get stack space */
1394 fstcw (%esp)
1395 popl %eax
1396 ret
1397
1398 /*
1399 * Set control word
1400 */
1401 ENTRY(_fldcw)
1402 fldcw 4(%esp)
1403 ret
1404
1405 /*
1406 * Read status word
1407 */
1408 ENTRY(_fnstsw)
1409 xor %eax,%eax /* clear high 16 bits of eax */
1410 fnstsw %ax /* read FP status */
1411 ret
1412
1413 /*
1414 * Clear FPU exceptions
1415 */
1416 ENTRY(_fnclex)
1417 fnclex
1418 ret
1419
1420 /*
1421 * Clear task-switched flag.
1422 */
1423 ENTRY(_clts)
1424 clts
1425 ret
1426
1427 /*
1428 * Save complete FPU state. Save error for later.
1429 */
1430 ENTRY(_fpsave)
1431 movl 4(%esp),%eax /* get save area pointer */
1432 fnsave (%eax) /* save complete state, including */
1433 /* errors */
1434 ret
1435
1436 /*
1437 * Restore FPU state.
1438 */
1439 ENTRY(_fprestore)
1440 movl 4(%esp),%eax /* get save area pointer */
1441 frstor (%eax) /* restore complete state */
1442 ret
1443
1444 /*
1445 * Set cr3
1446 */
1447 ENTRY(set_cr3)
1448 movl 4(%esp),%eax /* get new cr3 value */
1449 movl %eax,%cr3 /* load it */
1450 ret
1451
1452 /*
1453 * Read cr3
1454 */
1455 ENTRY(get_cr3)
1456 movl %cr3,%eax
1457 ret
1458
1459 /*
1460 * Flush TLB
1461 */
1462 ENTRY(flush_tlb)
1463 movl %cr3,%eax /* flush tlb by reloading CR3 */
1464 movl %eax,%cr3 /* with itself */
1465 ret
1466
1467 /*
1468 * Read cr2
1469 */
1470 ENTRY(get_cr2)
1471 movl %cr2,%eax
1472 ret
1473
1474 /*
1475 * Read ldtr
1476 */
1477 ENTRY(get_ldt)
1478 xorl %eax,%eax
1479 sldt %ax
1480 ret
1481
1482 /*
1483 * Set ldtr
1484 */
1485 ENTRY(set_ldt)
1486 lldt 4(%esp)
1487 ret
1488
1489 /*
1490 * Read task register.
1491 */
1492 ENTRY(get_tr)
1493 xorl %eax,%eax
1494 str %ax
1495 ret
1496
1497 /*
1498 * Set task register. Also clears busy bit of task descriptor.
1499 */
1500 ENTRY(set_tr)
1501 movl S_ARG0,%eax /* get task segment number */
1502 subl $8,%esp /* push space for SGDT */
1503 sgdt 2(%esp) /* store GDT limit and base (linear) */
1504 movl 4(%esp),%edx /* address GDT */
1505 movb $(K_TSS),5(%edx,%eax) /* fix access byte in task descriptor */
1506 ltr %ax /* load task register */
1507 addl $8,%esp /* clear stack */
1508 ret /* and return */
1509
1510 /*
1511 * Set task-switched flag.
1512 */
1513 ENTRY(_setts)
1514 movl %cr0,%eax /* get cr0 */
1515 orl $(CR0_TS),%eax /* or in TS bit */
1516 movl %eax,%cr0 /* set cr0 */
1517 ret
1518
1519 /*
1520 * void outb(unsigned char *io_port,
1521 * unsigned char byte)
1522 *
1523 * Output a byte to an IO port.
1524 */
1525 ENTRY(outb)
1526 movl S_ARG0,%edx /* IO port address */
1527 movl S_ARG1,%eax /* data to output */
1528 outb %al,%dx /* send it out */
1529 #ifdef iPSC386
1530 mull %ecx /* Delay a little to make H/W happy */
1531 #endif iPSC386
1532 ret
1533
1534 /*
1535 * unsigned char inb(unsigned char *io_port)
1536 *
1537 * Input a byte from an IO port.
1538 */
1539 ENTRY(inb)
1540 movl S_ARG0,%edx /* IO port address */
1541 xor %eax,%eax /* clear high bits of register */
1542 inb %dx,%al /* get the byte */
1543 #ifdef iPSC386
1544 / Do a long multiply to delay a little to make H/W happy. Must
1545 / save and restore EAX which is used to hold result of multiply
1546 pushl %eax
1547 mull %ecx
1548 popl %eax
1549 #endif iPSC386
1550 ret
1551
1552 /*
1553 * void outw(unsigned short *io_port,
1554 * unsigned short word)
1555 *
1556 * Output a word to an IO port.
1557 */
1558 ENTRY(outw)
1559 movl S_ARG0,%edx /* IO port address */
1560 movl S_ARG1,%eax /* data to output */
1561 outw %ax,%dx /* send it out */
1562 ret
1563
1564 /*
1565 * unsigned short inw(unsigned short *io_port)
1566 *
1567 * Input a word from an IO port.
1568 */
1569 ENTRY(inw)
1570 movl S_ARG0,%edx /* IO port address */
1571 xor %eax,%eax /* clear high bits of register */
1572 inw %dx,%ax /* get the word */
1573 ret
1574
1575 /*
1576 * void outl(unsigned int *io_port,
1577 * unsigned int byte)
1578 *
1579 * Output an int to an IO port.
1580 */
1581 ENTRY(outl)
1582 movl S_ARG0,%edx /* IO port address */
1583 movl S_ARG1,%eax /* data to output */
1584 outl %eax,%dx /* send it out */
1585 ret
1586
1587 /*
1588 * unsigned int inl(unsigned int *io_port)
1589 *
1590 * Input an int from an IO port.
1591 */
1592 ENTRY(inl)
1593 movl S_ARG0,%edx /* IO port address */
1594 inl %dx,%eax /* get the int */
1595 ret
1596
1597 /*
1598 * void loutb(unsigned byte *io_port,
1599 * unsigned byte *data,
1600 * unsigned int count)
1601 *
1602 * Output an array of bytes to an IO port.
1603 */
1604 ENTRY(loutb)
1605 movl %esi,%eax /* save register */
1606 movl S_ARG0,%edx /* get io port number */
1607 movl S_ARG1,%esi /* get data address */
1608 movl S_ARG2,%ecx /* get count */
1609
1610 cld /* count up */
1611
1612 rep
1613 outsb /* output */
1614
1615 movl %eax,%esi /* restore register */
1616 ret /* exit */
1617
1618
1619 /*
1620 * void loutw(unsigned short *io_port,
1621 * unsigned short *data,
1622 * unsigned int count)
1623 *
1624 * Output an array of shorts to an IO port.
1625 */
1626 ENTRY(loutw)
1627 movl %esi,%eax /* save register */
1628 movl S_ARG0,%edx /* get io port number */
1629 movl S_ARG1,%esi /* get data address */
1630 movl S_ARG2,%ecx /* get count */
1631
1632 cld /* count up */
1633
1634 rep
1635 outsw /* output */
1636
1637 movl %eax,%esi /* restore register */
1638 ret /* exit */
1639
1640
1641 /*
1642 * void linb(unsigned char *io_port,
1643 * unsigned char *data,
1644 * unsigned int count)
1645 *
1646 * Input an array of bytes from an IO port.
1647 */
1648 ENTRY(linb)
1649 movl %edi,%eax /* save register */
1650 movl S_ARG0,%edx /* get io port number */
1651 movl S_ARG1,%edi /* get data address */
1652 movl S_ARG2,%ecx /* get count */
1653
1654 cld /* count up */
1655
1656 rep
1657 insb /* input */
1658
1659 movl %eax,%edi /* restore register */
1660 ret /* exit */
1661
1662
1663 /*
1664 * void linw(unsigned short *io_port,
1665 * unsigned short *data,
1666 * unsigned int count)
1667 *
1668 * Input an array of shorts from an IO port.
1669 */
1670 ENTRY(linw)
1671 movl %edi,%eax /* save register */
1672 movl S_ARG0,%edx /* get io port number */
1673 movl S_ARG1,%edi /* get data address */
1674 movl S_ARG2,%ecx /* get count */
1675
1676 cld /* count up */
1677
1678 rep
1679 insw /* input */
1680
1681 movl %eax,%edi /* restore register */
1682 ret /* exit */
1683
1684
1685 /*
1686 * int inst_fetch(int eip, int cs);
1687 *
1688 * Fetch instruction byte. Return -1 if invalid address.
1689 */
1690 .globl _inst_fetch
1691 _inst_fetch:
1692 movl S_ARG1, %eax /* get segment */
1693 movw %ax,%fs /* into FS */
1694 movl S_ARG0, %eax /* get offset */
1695 RETRY(_inst_fetch) /* re-load FS on retry */
1696 RECOVER(_inst_fetch_fault)
1697 movzbl %fs:(%eax),%eax /* load instruction byte */
1698 ret
1699
1700 _inst_fetch_fault:
1701 movl $-1,%eax /* return -1 if error */
1702 ret
1703
1704
1705 /*
1706 * Done with recovery and retry tables.
1707 */
1708 RECOVER_TABLE_END
1709 RETRY_TABLE_END
1710
1711
1712
1713 ENTRY(dr6)
1714 movl %db6, %eax
1715 ret
1716
1717 /* dr<i>(address, type, len, persistence)
1718 */
1719 ENTRY(dr0)
1720 movl S_ARG0, %eax
1721 movl %eax,_dr_addr
1722 movl %eax, %db0
1723 movl $0, %ecx
1724 jmp 0f
1725 ENTRY(dr1)
1726 movl S_ARG0, %eax
1727 movl %eax,_dr_addr+1*4
1728 movl %eax, %db1
1729 movl $2, %ecx
1730 jmp 0f
1731 ENTRY(dr2)
1732 movl S_ARG0, %eax
1733 movl %eax,_dr_addr+2*4
1734 movl %eax, %db2
1735 movl $4, %ecx
1736 jmp 0f
1737
1738 ENTRY(dr3)
1739 movl S_ARG0, %eax
1740 movl %eax,_dr_addr+3*4
1741 movl %eax, %db3
1742 movl $6, %ecx
1743
1744 0:
1745 pushl %ebp
1746 movl %esp, %ebp
1747
1748 movl %db7, %edx
1749 movl %edx,_dr_addr+4*4
1750 andl dr_msk(,%ecx,2),%edx /* clear out new entry */
1751 movl %edx,_dr_addr+5*4
1752 movzbl B_ARG3, %eax
1753 andb $3, %al
1754 shll %cl, %eax
1755 orl %eax, %edx
1756
1757 movzbl B_ARG1, %eax
1758 andb $3, %al
1759 addb $0x10, %ecx
1760 shll %cl, %eax
1761 orl %eax, %edx
1762
1763 movzbl B_ARG2, %eax
1764 andb $3, %al
1765 addb $0x2, %ecx
1766 shll %cl, %eax
1767 orl %eax, %edx
1768
1769 movl %edx, %db7
1770 movl %edx,_dr_addr+7*4
1771 movl %edx, %eax
1772 leave
1773 ret
1774
1775 .data
1776 dr_msk:
1777 .long ~0x000f0003
1778 .long ~0x00f0000c
1779 .long ~0x0f000030
1780 .long ~0xf00000c0
1781 ENTRY(dr_addr)
1782 .long 0,0,0,0
1783 .long 0,0,0,0
1784 .text
1785
1786 /*
1787 * Waste 10 microseconds.
1788 */
1789 ENTRY(tenmicrosec)
1790 movl _microdata,%ecx /* cycle count for 10 microsecond loop */
1791 tenmicroloop:
1792 loop tenmicroloop
1793 ret
1794
Cache object: 7b5afd01df128d7ab08188fabbc5d9fc
|