| /* |
| * linux/kernel/sys_call.S |
| * |
| * (C) 1991 Linus Torvalds |
| */ |
| |
| /* |
| * sys_call.S contains the system-call and fault low-level handling routines. |
| * This also contains the timer-interrupt handler, as well as all interrupts |
| * and faults that can result in a task-switch. |
| * |
| * NOTE: This code handles signal-recognition, which happens every time |
| * after a timer-interrupt and after each system call. |
| * |
| * Stack layout in 'ret_from_system_call': |
| * ptrace needs to have all regs on the stack. |
| * if the order here is changed, it needs to be |
| * updated in fork.c:copy_process, signal.c:do_signal, |
| * ptrace.c and ptrace.h |
| * |
| * 0(%esp) - %ebx |
| * 4(%esp) - %ecx |
| * 8(%esp) - %edx |
| * C(%esp) - %esi |
| * 10(%esp) - %edi |
| * 14(%esp) - %ebp |
| * 18(%esp) - %eax |
| * 1C(%esp) - %ds |
| * 20(%esp) - %es |
| * 24(%esp) - %fs |
| * 28(%esp) - %gs |
| * 2C(%esp) - orig_eax |
| * 30(%esp) - %eip |
| * 34(%esp) - %cs |
| * 38(%esp) - %eflags |
| * 3C(%esp) - %oldesp |
| * 40(%esp) - %oldss |
| */ |
| |
| SIG_CHLD = 17 |
| |
| EBX = 0x00 |
| ECX = 0x04 |
| EDX = 0x08 |
| ESI = 0x0C |
| EDI = 0x10 |
| EBP = 0x14 |
| EAX = 0x18 |
| DS = 0x1C |
| ES = 0x20 |
| FS = 0x24 |
| GS = 0x28 |
| ORIG_EAX = 0x2C |
| EIP = 0x30 |
| CS = 0x34 |
| EFLAGS = 0x38 |
| OLDESP = 0x3C |
| OLDSS = 0x40 |
| |
| /* |
| * these are offsets into the task-struct. |
| */ |
| state = 0 |
| counter = 4 |
| priority = 8 |
| signal = 12 |
| sigaction = 16 # MUST be 16 (=len of sigaction) |
| blocked = (33*16) |
| |
| /* |
| * offsets within sigaction |
| */ |
| sa_handler = 0 |
| sa_mask = 4 |
| sa_flags = 8 |
| sa_restorer = 12 |
| |
| ENOSYS = 38 |
| |
| /* |
| * Ok, I get parallel printer interrupts while using the floppy for some |
| * strange reason. Urgel. Now I just ignore them. |
| */ |
| .globl _system_call,_timer_interrupt,_sys_execve |
| .globl _device_not_available, _coprocessor_error |
| .globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op |
| .globl _double_fault,_coprocessor_segment_overrun |
| .globl _invalid_TSS,_segment_not_present,_stack_segment |
| .globl _general_protection,_irq13,_reserved |
| .globl _alignment_check,_page_fault |
| .globl _keyboard_interrupt |
| |
| #define SAVE_ALL \ |
| cld; \ |
| push %gs; \ |
| push %fs; \ |
| push %es; \ |
| push %ds; \ |
| pushl %eax; \ |
| pushl %ebp; \ |
| pushl %edi; \ |
| pushl %esi; \ |
| pushl %edx; \ |
| pushl %ecx; \ |
| pushl %ebx; \ |
| movl $0x10,%edx; \ |
| mov %dx,%ds; \ |
| mov %dx,%es; \ |
| movl $0x17,%edx; \ |
| mov %dx,%fs |
| |
| #define ACK_FIRST(mask) \ |
| inb $0x21,%al; \ |
| jmp 1f; \ |
| 1: jmp 1f; \ |
| 1: orb $(mask),%al; \ |
| outb %al,$0x21; \ |
| jmp 1f; \ |
| 1: jmp 1f; \ |
| 1: movb $0x20,%al; \ |
| outb %al,$0x20 |
| |
| #define ACK_SECOND(mask) \ |
| inb $0xA1,%al; \ |
| jmp 1f; \ |
| 1: jmp 1f; \ |
| 1: orb $mask,%al; \ |
| outb %al,$0xA1; \ |
| jmp 1f; \ |
| 1: jmp 1f; \ |
| 1: movb $0x20,%al; \ |
| outb %al,$0x20 |
| jmp 1f; \ |
| 1: jmp 1f; \ |
| 1: outb %al,$0xA0 |
| |
| #define UNBLK_FIRST(mask) \ |
| inb $0x21,%al; \ |
| jmp 1f; \ |
| 1: jmp 1f; \ |
| 1: andb $~(mask),%al; \ |
| outb %al,$0x21 |
| |
| #define UNBLK_SECOND(mask) \ |
| inb $0xA1,%al; \ |
| jmp 1f; \ |
| 1: jmp 1f; \ |
| 1: andb $~(mask),%al; \ |
| outb %al,$0xA1 |
| |
| .align 2 |
| bad_sys_call: |
| movl $-ENOSYS,EAX(%esp) |
| jmp ret_from_sys_call |
| .align 2 |
| reschedule: |
| pushl $ret_from_sys_call |
| jmp _schedule |
| .align 2 |
| _system_call: |
| pushl %eax # save orig_eax |
| SAVE_ALL |
| cmpl _NR_syscalls,%eax |
| jae bad_sys_call |
| call _sys_call_table(,%eax,4) |
| movl %eax,EAX(%esp) # save the return value |
| ret_from_sys_call: |
| cmpw $0x0f,CS(%esp) # was old code segment supervisor ? |
| jne 2f |
| cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? |
| jne 2f |
| 1: movl _current,%eax |
| cmpl _task,%eax # task[0] cannot have signals |
| je 2f |
| cmpl $0,state(%eax) # state |
| jne reschedule |
| cmpl $0,counter(%eax) # counter |
| je reschedule |
| movl signal(%eax),%ebx |
| movl blocked(%eax),%ecx |
| notl %ecx |
| andl %ebx,%ecx |
| bsfl %ecx,%ecx |
| je 2f |
| btrl %ecx,%ebx |
| movl %ebx,signal(%eax) |
| incl %ecx |
| pushl %ecx |
| call _do_signal |
| popl %ecx |
| testl %eax, %eax |
| jne 1b # see if we need to switch tasks, or do more signals |
| 2: popl %ebx |
| popl %ecx |
| popl %edx |
| popl %esi |
| popl %edi |
| popl %ebp |
| popl %eax |
| pop %ds |
| pop %es |
| pop %fs |
| pop %gs |
| addl $4,%esp # skip the orig_eax |
| iret |
| |
| .align 2 |
| _irq13: |
| pushl %eax |
| xorb %al,%al |
| outb %al,$0xF0 |
| movb $0x20,%al |
| outb %al,$0x20 |
| jmp 1f |
| 1: jmp 1f |
| 1: outb %al,$0xA0 |
| popl %eax |
| _coprocessor_error: |
| pushl $-1 # mark this as an int. |
| SAVE_ALL |
| pushl $ret_from_sys_call |
| jmp _math_error |
| |
| .align 2 |
| _device_not_available: |
| pushl $-1 # mark this as an int |
| SAVE_ALL |
| pushl $ret_from_sys_call |
| clts # clear TS so that we can use math |
| movl %cr0,%eax |
| testl $0x4,%eax # EM (math emulation bit) |
| je _math_state_restore |
| pushl $0 # temporary storage for ORIG_EIP |
| call _math_emulate |
| addl $4,%esp |
| ret |
| |
| .align 2 |
| _keyboard_interrupt: |
| pushl $-1 |
| SAVE_ALL |
| ACK_FIRST(2) |
| sti |
| call _do_keyboard |
| cli |
| UNBLK_FIRST(2) |
| jmp ret_from_sys_call |
| |
| .align 2 |
| _timer_interrupt: |
| pushl $-1 # mark this as an int |
| SAVE_ALL |
| incl _jiffies |
| movb $0x20,%al # EOI to interrupt controller #1 |
| outb %al,$0x20 |
| movl CS(%esp),%eax |
| andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor) |
| pushl %eax |
| call _do_timer # 'do_timer(long CPL)' does everything from |
| addl $4,%esp # task switching to accounting ... |
| jmp ret_from_sys_call |
| |
| .align 2 |
| _sys_execve: |
| lea (EIP+4)(%esp),%eax # don't forget about the return address. |
| pushl %eax |
| call _do_execve |
| addl $4,%esp |
| ret |
| |
| _divide_error: |
| pushl $0 # no error code |
| pushl $_do_divide_error |
| error_code: |
| push %fs |
| push %es |
| push %ds |
| pushl %eax |
| pushl %ebp |
| pushl %edi |
| pushl %esi |
| pushl %edx |
| pushl %ecx |
| pushl %ebx |
| cld |
| movl $-1, %eax |
| xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. ) |
| xorl %ebx,%ebx # zero ebx |
| mov %gs,%bx # get the lower order bits of gs |
| xchgl %ebx, GS(%esp) # get the address and save gs. |
| pushl %eax # push the error code |
| lea 52(%esp),%edx |
| pushl %edx |
| movl $0x10,%edx |
| mov %dx,%ds |
| mov %dx,%es |
| movl $0x17,%edx |
| mov %dx,%fs |
| call *%ebx |
| addl $8,%esp |
| jmp ret_from_sys_call |
| |
| _debug: |
| pushl $0 |
| pushl $_do_int3 # _do_debug |
| jmp error_code |
| |
| _nmi: |
| pushl $0 |
| pushl $_do_nmi |
| jmp error_code |
| |
| _int3: |
| pushl $0 |
| pushl $_do_int3 |
| jmp error_code |
| |
| _overflow: |
| pushl $0 |
| pushl $_do_overflow |
| jmp error_code |
| |
| _bounds: |
| pushl $0 |
| pushl $_do_bounds |
| jmp error_code |
| |
| _invalid_op: |
| pushl $0 |
| pushl $_do_invalid_op |
| jmp error_code |
| |
| _coprocessor_segment_overrun: |
| pushl $0 |
| pushl $_do_coprocessor_segment_overrun |
| jmp error_code |
| |
| _reserved: |
| pushl $0 |
| pushl $_do_reserved |
| jmp error_code |
| |
| _double_fault: |
| pushl $_do_double_fault |
| jmp error_code |
| |
| _invalid_TSS: |
| pushl $_do_invalid_TSS |
| jmp error_code |
| |
| _segment_not_present: |
| pushl $_do_segment_not_present |
| jmp error_code |
| |
| _stack_segment: |
| pushl $_do_stack_segment |
| jmp error_code |
| |
| _general_protection: |
| pushl $_do_general_protection |
| jmp error_code |
| |
| _alignment_check: |
| pushl $_do_alignment_check |
| jmp error_code |
| |
| _page_fault: |
| pushl $_do_page_fault |
| jmp error_code |