220 lines
4.9 KiB
ArmAsm
220 lines
4.9 KiB
ArmAsm
/*
|
|
* system_call.s contains the system-call low-level handling routines.
|
|
* This also contains the timer-interrupt handler, as some of the code is
|
|
* the same. The hd-interrupt is also here.
|
|
*
|
|
* NOTE: This code handles signal-recognition, which happens every time
|
|
* after a timer-interrupt and after each system call. Ordinary interrupts
|
|
* don't handle signal-recognition, as that would clutter them up totally
|
|
* unnecessarily.
|
|
*
|
|
* Stack layout in 'ret_from_system_call':
|
|
*
|
|
* 0(%esp) - %eax
|
|
* 4(%esp) - %ebx
|
|
* 8(%esp) - %ecx
|
|
* C(%esp) - %edx
|
|
* 10(%esp) - %fs
|
|
* 14(%esp) - %es
|
|
* 18(%esp) - %ds
|
|
* 1C(%esp) - %eip
|
|
* 20(%esp) - %cs
|
|
* 24(%esp) - %eflags
|
|
* 28(%esp) - %oldesp
|
|
* 2C(%esp) - %oldss
|
|
*/
|
|
|
|
SIG_CHLD = 17
|
|
EAX = 0x00
|
|
EBX = 0x04
|
|
ECX = 0x08
|
|
EDX = 0x0C
|
|
FS = 0x10
|
|
ES = 0x14
|
|
DS = 0x18
|
|
EIP = 0x1C
|
|
CS = 0x20
|
|
EFLAGS = 0x24
|
|
OLDESP = 0x28
|
|
OLDSS = 0x2C
|
|
|
|
state = 0 # these are offsets into the task-struct.
|
|
counter = 4
|
|
priority = 8
|
|
signal = 12
|
|
restorer = 16 # address of info-restorer
|
|
sig_fn = 20 # table of 32 signal addresses
|
|
|
|
nr_system_calls = 67
|
|
|
|
.globl _system_call,_sys_fork,_timer_interrupt,_hd_interrupt,_sys_execve
|
|
|
|
.align 2
|
|
bad_sys_call:
|
|
movl $-1,%eax
|
|
iret
|
|
.align 2
|
|
reschedule:
|
|
pushl $ret_from_sys_call
|
|
jmp _schedule
|
|
.align 2
|
|
_system_call:
|
|
cmpl $nr_system_calls-1,%eax
|
|
ja bad_sys_call
|
|
push %ds
|
|
push %es
|
|
push %fs
|
|
pushl %edx
|
|
pushl %ecx # push %ebx,%ecx,%edx as parameters
|
|
pushl %ebx # to the system call
|
|
movl $0x10,%edx # set up ds,es to kernel space
|
|
mov %dx,%ds
|
|
mov %dx,%es
|
|
movl $0x17,%edx # fs points to local data space
|
|
mov %dx,%fs
|
|
call _sys_call_table(,%eax,4)
|
|
pushl %eax
|
|
movl _current,%eax
|
|
cmpl $0,state(%eax) # state
|
|
jne reschedule
|
|
cmpl $0,counter(%eax) # counter
|
|
je reschedule
|
|
ret_from_sys_call:
|
|
movl _current,%eax # task[0] cannot have signals
|
|
cmpl _task,%eax
|
|
je 3f
|
|
movl CS(%esp),%ebx # was old code segment supervisor
|
|
testl $3,%ebx # mode? If so - don't check signals
|
|
je 3f
|
|
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
|
|
jne 3f
|
|
2: movl signal(%eax),%ebx # signals (bitmap, 32 signals)
|
|
bsfl %ebx,%ecx # %ecx is signal nr, return if none
|
|
je 3f
|
|
btrl %ecx,%ebx # clear it
|
|
movl %ebx,signal(%eax)
|
|
movl sig_fn(%eax,%ecx,4),%ebx # %ebx is signal handler address
|
|
cmpl $1,%ebx
|
|
jb default_signal # 0 is default signal handler - exit
|
|
je 2b # 1 is ignore - find next signal
|
|
movl $0,sig_fn(%eax,%ecx,4) # reset signal handler address
|
|
incl %ecx
|
|
xchgl %ebx,EIP(%esp) # put new return address on stack
|
|
subl $28,OLDESP(%esp)
|
|
movl OLDESP(%esp),%edx # push old return address on stack
|
|
pushl %eax # but first check that it's ok.
|
|
pushl %ecx
|
|
pushl $28
|
|
pushl %edx
|
|
call _verify_area
|
|
popl %edx
|
|
addl $4,%esp
|
|
popl %ecx
|
|
popl %eax
|
|
movl restorer(%eax),%eax
|
|
movl %eax,%fs:(%edx) # flag/reg restorer
|
|
movl %ecx,%fs:4(%edx) # signal nr
|
|
movl EAX(%esp),%eax
|
|
movl %eax,%fs:8(%edx) # old eax
|
|
movl ECX(%esp),%eax
|
|
movl %eax,%fs:12(%edx) # old ecx
|
|
movl EDX(%esp),%eax
|
|
movl %eax,%fs:16(%edx) # old edx
|
|
movl EFLAGS(%esp),%eax
|
|
movl %eax,%fs:20(%edx) # old eflags
|
|
movl %ebx,%fs:24(%edx) # old return addr
|
|
3: popl %eax
|
|
popl %ebx
|
|
popl %ecx
|
|
popl %edx
|
|
pop %fs
|
|
pop %es
|
|
pop %ds
|
|
iret
|
|
|
|
default_signal:
|
|
incl %ecx
|
|
cmpl $SIG_CHLD,%ecx
|
|
je 2b
|
|
pushl %ecx
|
|
call _do_exit # remember to set bit 7 when dumping core
|
|
addl $4,%esp
|
|
jmp 3b
|
|
|
|
.align 2
|
|
_timer_interrupt:
|
|
push %ds # save ds,es and put kernel data space
|
|
push %es # into them. %fs is used by _system_call
|
|
push %fs
|
|
pushl %edx # we save %eax,%ecx,%edx as gcc doesn't
|
|
pushl %ecx # save those across function calls. %ebx
|
|
pushl %ebx # is saved as we use that in ret_sys_call
|
|
pushl %eax
|
|
movl $0x10,%eax
|
|
mov %ax,%ds
|
|
mov %ax,%es
|
|
movl $0x17,%eax
|
|
mov %ax,%fs
|
|
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(%esp),%eax
|
|
pushl %eax
|
|
call _do_execve
|
|
addl $4,%esp
|
|
ret
|
|
|
|
.align 2
|
|
_sys_fork:
|
|
call _find_empty_process
|
|
testl %eax,%eax
|
|
js 1f
|
|
push %gs
|
|
pushl %esi
|
|
pushl %edi
|
|
pushl %ebp
|
|
pushl %eax
|
|
call _copy_process
|
|
addl $20,%esp
|
|
1: ret
|
|
|
|
_hd_interrupt:
|
|
pushl %eax
|
|
pushl %ecx
|
|
pushl %edx
|
|
push %ds
|
|
push %es
|
|
push %fs
|
|
movl $0x10,%eax
|
|
mov %ax,%ds
|
|
mov %ax,%es
|
|
movl $0x17,%eax
|
|
mov %ax,%fs
|
|
movb $0x20,%al
|
|
outb %al,$0x20 # EOI to interrupt controller #1
|
|
jmp 1f # give port chance to breathe
|
|
1: jmp 1f
|
|
1: outb %al,$0xA0 # same to controller #2
|
|
movl _do_hd,%eax
|
|
testl %eax,%eax
|
|
jne 1f
|
|
movl $_unexpected_hd_interrupt,%eax
|
|
1: call *%eax # "interesting" way of handling intr.
|
|
pop %fs
|
|
pop %es
|
|
pop %ds
|
|
popl %edx
|
|
popl %ecx
|
|
popl %eax
|
|
iret
|
|
|