/* $Id: entry.S,v 1.19 2000/01/12 19:19:56 bjornw Exp $
 *
 *  linux/arch/etrax100/entry.S
 *
 *  Copyright (C) 1998 Axis Communications AB
 */

/*
 * entry.S contains the system-call and fault low-level handling routines.
 *
 * 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
 *
 */
	
#include <linux/linkage.h>
#include <linux/sys.h>
#include <asm/segment.h>
	
	;; functions exported from this file
	
	.globl _system_call
	.globl _ret_from_sys_call
	.globl _resume
	.globl _multiple_interrupt
	.globl _hwbreakpoint
	.globl _IRQ1_interrupt
	.globl _timer_interrupt
	.globl _timer_shortcut
	.globl _spurious_interrupt
	.globl _hw_bp_trigs
			
	.globl _sys_call_table
		
	;; syscall error codes
	
LENOSYS = 38

	;; offsets into the task_struct
	
LTASK_STATE     =  0
LTASK_COUNTER   =  4
LTASK_PRIORITY  =  8
LTASK_SIGNAL    = 12
LTASK_BLOCKED   = 16
LTASK_FLAGS     = 20
	
	;; pt_regs offsets (from ptrace.h)
	
LR10		= 16
LORIG_R10	= 0
LDCCR		= 60
LSRP		= 64
LIRP		= 68

	;; handle "software irqs" aka bottom halves
				
handle_bottom_half:
	push	r9
	
	move	ccr,r1
	di
	move.d	[_intr_count],r0
	addq	1,r0		; this operation needs to be atomic! thats why the di is there
	move.d	r0,[_intr_count]
	move	r1,ccr
	
	jsr	_do_bottom_half		; call the C routine for bottom half handling

	di
	move.d	[_intr_count],r0
	subq	1,r0		; this operation needs to be atomic!
	move.d	r0,[_intr_count]
	
	pop	r9
	ba	ret_from_handle_bh
	nop
	
	;; ret_from_sys_call decided we need to process signals
	;; in r9 we have an argument on whether we came from syscall (1) or irq (0)
	;; do_signal needs that to know if it can try to restart the syscall.
	
handle_signals:
	jsr	_do_signal
	ba	Rexit
	nop
	
reschedule:
	push	r9
	jsr	_schedule
	pop	r9
	ba	_ret_from_sys_call
	nop

	;; The system_call is called by a JIR instruction, which works exactly
	;; like a normal Etrax IRQ - that is, it doesn't do anything except IRP=IP.
	;; In r1 we have the wanted syscall number. Arguments come in r10,r11,r12,r13,r0

_system_call:
	;; make a stack-frame similar to the irq heads, which is reversed in ret_from_sys_call
	push	irp
	push	srp
	push	dccr
	subq	14*4,sp		; make room for r0-r13
	movem	r13,[sp]	; push r0-r13
	push	r10		; push orig_r10

	move.d	r10,r2		; save for later
	
	move.d	-LENOSYS,r10
	move.d	r10,[sp+LR10]	; put the default return value in r10 in the frame
	
	move.d	sp,r10
	jsr	_set_esp0	; save top of frame (clobbers r9...)

	cmpu.w	NR_syscalls,r1	; check for sanity in the requested syscall number
	bcc	_ret_from_sys_call
	lslq	2,r1		;  multiply by 4, in the delay slot
	
	move.d	[r1+_sys_call_table],r1	; read the system call vector into r1
	beq	_ret_from_sys_call	; skip if not implemented
	nop

	;; now we should really check for the PF_TRACESYS flag in the task_flags entry
	;; but we don't - so we can't trace processes yet

	;; the parameter carrying registers r11, r12 and 13 are intact - restore r10.
	;; the fifth parameter (if any) was in r0, and we need to put it on the stack

	push	r0
	move.d	r2,r10
	
	jsr	r1		; actually call the corresponding system call
	addq	4,sp		; pop the r0 parameter
	move.d	r10,[sp+LR10]	; save the return value

	moveq	1,r9		; "parameter" to ret_from_sys_call to show it was a sys call
	
	;; fall through into ret_from_sys_call to return
	
_ret_from_sys_call:
	;; r9 is a parameter - if 1, we came from a syscall, if 0, from an interrupt
		
	;; first check for nested IRQ's and skip everything if so 
	
	test.d	[_intr_count]
	bne	Rexit
	nop

	;; check if any bottom halves need service
	
ret_from_handle_bh:

	move.d	[_bh_mask],r0
	and.d	[_bh_active],r0,r0 
	bne	handle_bottom_half
	nop

	;; here we need to check if the previous context was a kernel or user context
	;; since Etrax doesn't have a supervisor bit, and not a code segment, we need
	;; to use a nice hack - we check if the previous context's instruction pointer
	;; was below the highest kernel code adress we know -bw

	move.d	_edata,r0	; should be _etext, but to avoid possible linker/compiler errors..
	cmp.d	[sp+LIRP],r0
	bcc	Rexit		; skip rescheduling and signal check if we were in kernel segment
	nop
	
	ei			; from now on we can be interrupted again

	;; see if we want to reschedule into another process
	
	test.d	[_need_resched]
	bne	reschedule
	nop
	
	;; if the running task is the init_task (stored in task[0]) dont check for signals
	;; because it doesn't want them.. it can't be killed
	
	move.d	[_current_set],r0
	cmp.d	[_task],r0
	beq	Rexit
	nop
	
	;; check if any non-blocked signals are raised and need to be processed
	;; r0 contains the ptr to the current task
	;; r9 contains the distinction between syscall and irq, since far above
	
	move.d	[r0+LTASK_BLOCKED],r1
	move.d	r9,r10		; do_signals syscall/irq param
	move.d	r1,r11		; save current->blocked for do_signal (oldmask param)
	move.d	sp,r12		; another argument to do_signal (the regs param)
	not	r1
	and.d	[r0+LTASK_SIGNAL],r1
	bne	handle_signals
	nop
	
Rexit:
	;; this epilogue MUST match the prologues in multiple_interrupt, irq.h and ptregs.h !!!
	addq	4,sp		; skip orig_r10
	movem	[sp+],r13	; pop registers r0-r13
	pop	dccr		; pop condition codes
	pop	srp		; pop subroutine return pointer
	jump	[sp+]		; return by popping irp and jumping there

LTSS_USP	= 0
LTSS_ESP0	= 4
LTSS_CCR	= 8
	
	;; _resume performs the actual task-switching, by switching stack pointers
	;; input arguments: r10 = prev, r11 = next, r12 = tss offset in task struct
	
_resume:	
	push	srp
	add.d	r12,r10			; r10 = current tasks tss
	move	ccr,[r10+LTSS_CCR]	; save irq enable state (ccr)
	di

	subq	10*4,sp
	movem	r9,[sp]
	
	move.d	sp,[r10+LTSS_USP]	; save the stack pointer for the old task
	move.d	r11,[_current_set]	; make the new task the current task
	add.d	r12,r11			; and find the new tasks tss
	move.d	[r11+LTSS_USP],sp	; here we actually switch into the new stackframe

	movem	[sp+],r9
		
	move	[r11+LTSS_CCR],ccr	; restore irq enable status (ccr)
	jump	[sp+]

	;; this is the timer interrupt, it's specially coded here
	;; the difference from a normal IRQ is that this never does an ei, yet it
	;; calls the ret_from_sys_call at the end.
	
_timer_interrupt:
	push	irp
	push	srp
	push	dccr
	di
	subq	14*4,sp
	movem	r13,[sp]	; push r0-r13
	push	r10		; push orig_r10
_timer_shortcut:		; entry used by the multiple interrupt handler
	moveq	4,r0		; block timer irq
	move.d	r0,[0xb00000d8]
	
	move.d	[_intr_count],r0
	addq	1,r0
	move.d	r0,[_intr_count]

	moveq	2,r10
	move.d	sp,r11
	jsr	_do_IRQ		; r10 and r11 are arguments to this C-function
	di			; to be sure, if the c-code does anything stupid
	
	move.d	[_intr_count],r0
	subq	1,r0
	move.d	r0,[_intr_count]

	moveq	4,r0		; unblock timer irq
	move.d	r0,[0xb00000dc]

	moveq	0,r9		; make ret_from_sys_call realise we came from an irq
	jump	_ret_from_sys_call
	
	;; special handlers for breakpoint and NMI
#if 0			
_hwbreakpoint:
	push	dccr
	di
	push	r10
	push	r11
	push	r12
	push	r13
	clearf	b
	move	brp,r11
	move.d	[_hw_bp_msg],r10
	jsr	_printk
	setf	b
	pop	r13
	pop	r12
	pop	r11
	pop	r10
	pop	dccr
	retb
	nop
#else
_hwbreakpoint:
	push	dccr
	di
#if 1
	push	r10
	push	r11
	move.d	[_hw_bp_trig_ptr],r10
	move.d	[r10],r11
	cmp.d	42,r11
	beq	nobp
	nop
	move	brp,r11
	move.d	r11,[r10+]
	move.d	r10,[_hw_bp_trig_ptr]
nobp:	pop	r11
	pop	r10
#endif
	pop	dccr
	retb
	nop
#endif
	
_IRQ1_interrupt:
_spurious_interrupt:	
	di
	move.b	4,r0
	move.b	r0,[0xb0000030]
basse2:	ba	basse2
	nop

	;; this handles the case when multiple interrupts arrive at the same time
	;; we jump to the first set interrupt bit in a priority fashion
	;; the hardware will call the unserved interrupts after the handler finishes
	
_multiple_interrupt:
	;; this prologue MUST match the one in irq.h and the struct in ptregs.h!!!
	push	irp
	push	srp
	push	dccr
	di
	subq	14*4,sp
	movem	r13,[sp]
	push	r10		; push orig_r10

	move.d	_irq_shortcuts + 8,r1
	moveq	2,r2		; first bit we care about is the timer0 irq
	move.d	[0xb00000d8],r0	; read the irq bits that triggered the multiple irq
multloop:	
	btst	r2,r0		; check for the irq given by bit r2
	bmi	do_shortcut	; actually do the shortcut
	nop
	addq	1,r2		; next vector bit - remember this is in the delay slot!
	addq	4,r1		; next vector
	cmpq	26,r2
	bne	multloop	; process all irq's up to and including number 25
	nop
	
	;; strange, we didn't get any set vector bits.. oh well, just return
	
	ba	Rexit
	nop

do_shortcut:
	test.d	[r1]
	beq	Rexit
	nop
	jump	[r1]		; jump to the irq handlers shortcut

	
	.data

_hw_bp_trigs:
	.space 64*4
_hw_bp_trig_ptr:
	.dword _hw_bp_trigs
	
#define NO_BASSE 1
			
_sys_call_table:	
	.long SYMBOL_NAME(sys_setup)		/* 0 */
	.long SYMBOL_NAME(sys_exit)
	.long SYMBOL_NAME(sys_fork)
#ifndef NO_BASSE
	.long _sys_basse
#else
	.long SYMBOL_NAME(sys_read)
#endif
	.long SYMBOL_NAME(sys_write)
	.long SYMBOL_NAME(sys_open)		/* 5 */
	.long SYMBOL_NAME(sys_close)
	.long SYMBOL_NAME(sys_waitpid)
	.long SYMBOL_NAME(sys_creat)
	.long SYMBOL_NAME(sys_link)
	.long SYMBOL_NAME(sys_unlink)		/* 10 */
	.long SYMBOL_NAME(sys_execve)
	.long SYMBOL_NAME(sys_chdir)
	.long SYMBOL_NAME(sys_time)
	.long SYMBOL_NAME(sys_mknod)
	.long SYMBOL_NAME(sys_chmod)		/* 15 */
	.long SYMBOL_NAME(sys_chown)
	.long SYMBOL_NAME(sys_break)
	.long SYMBOL_NAME(sys_stat)
	.long SYMBOL_NAME(sys_lseek)
	.long SYMBOL_NAME(sys_getpid)		/* 20 */
	.long SYMBOL_NAME(sys_mount)
	.long SYMBOL_NAME(sys_umount)
	.long SYMBOL_NAME(sys_setuid)
	.long SYMBOL_NAME(sys_getuid)
	.long SYMBOL_NAME(sys_stime)		/* 25 */
	.long SYMBOL_NAME(sys_ptrace)
	.long SYMBOL_NAME(sys_alarm)
	.long SYMBOL_NAME(sys_fstat)
	.long SYMBOL_NAME(sys_pause)
	.long SYMBOL_NAME(sys_utime)		/* 30 */
	.long SYMBOL_NAME(sys_stty)
	.long SYMBOL_NAME(sys_gtty)
	.long SYMBOL_NAME(sys_access)
	.long SYMBOL_NAME(sys_nice)
	.long SYMBOL_NAME(sys_ftime)		/* 35 */
	.long SYMBOL_NAME(sys_sync)
	.long SYMBOL_NAME(sys_kill)
	.long SYMBOL_NAME(sys_rename)
	.long SYMBOL_NAME(sys_mkdir)
	.long SYMBOL_NAME(sys_rmdir)		/* 40 */
	.long SYMBOL_NAME(sys_dup)
	.long SYMBOL_NAME(sys_pipe)
	.long SYMBOL_NAME(sys_times)
	.long SYMBOL_NAME(sys_prof)
	.long SYMBOL_NAME(sys_brk)		/* 45 */
	.long SYMBOL_NAME(sys_setgid)
	.long SYMBOL_NAME(sys_getgid)
	.long SYMBOL_NAME(sys_signal)
	.long SYMBOL_NAME(sys_geteuid)
	.long SYMBOL_NAME(sys_getegid)		/* 50 */
	.long SYMBOL_NAME(sys_acct)
	.long SYMBOL_NAME(sys_phys)
	.long SYMBOL_NAME(sys_lock)
	.long SYMBOL_NAME(sys_ioctl)
	.long SYMBOL_NAME(sys_fcntl)		/* 55 */
	.long SYMBOL_NAME(sys_mpx)
	.long SYMBOL_NAME(sys_setpgid)
	.long SYMBOL_NAME(sys_ulimit)
	.long SYMBOL_NAME(sys_olduname)
	.long SYMBOL_NAME(sys_umask)		/* 60 */
	.long SYMBOL_NAME(sys_chroot)
	.long SYMBOL_NAME(sys_ustat)
	.long SYMBOL_NAME(sys_dup2)
	.long SYMBOL_NAME(sys_getppid)
	.long SYMBOL_NAME(sys_getpgrp)		/* 65 */
	.long SYMBOL_NAME(sys_setsid)
	.long SYMBOL_NAME(sys_sigaction)
	.long SYMBOL_NAME(sys_sgetmask)
	.long SYMBOL_NAME(sys_ssetmask)
	.long SYMBOL_NAME(sys_setreuid)		/* 70 */
	.long SYMBOL_NAME(sys_setregid)
	.long SYMBOL_NAME(sys_sigsuspend)
	.long SYMBOL_NAME(sys_sigpending)
	.long SYMBOL_NAME(sys_sethostname)
	.long SYMBOL_NAME(sys_setrlimit)	/* 75 */
	.long SYMBOL_NAME(sys_getrlimit)
	.long SYMBOL_NAME(sys_getrusage)
	.long SYMBOL_NAME(sys_gettimeofday)
	.long SYMBOL_NAME(sys_settimeofday)
	.long SYMBOL_NAME(sys_getgroups)	/* 80 */
	.long SYMBOL_NAME(sys_setgroups)
	.long SYMBOL_NAME(old_select)
	.long SYMBOL_NAME(sys_symlink)
	.long SYMBOL_NAME(sys_lstat)
	.long SYMBOL_NAME(sys_readlink)		/* 85 */
	.long SYMBOL_NAME(sys_uselib)
	.long SYMBOL_NAME(sys_swapon)
	.long SYMBOL_NAME(sys_reboot)
	.long SYMBOL_NAME(old_readdir)
	.long SYMBOL_NAME(old_mmap)		/* 90 */
	.long SYMBOL_NAME(sys_munmap)
	.long SYMBOL_NAME(sys_truncate)
	.long SYMBOL_NAME(sys_ftruncate)
	.long SYMBOL_NAME(sys_fchmod)
	.long SYMBOL_NAME(sys_fchown)		/* 95 */
	.long SYMBOL_NAME(sys_getpriority)
	.long SYMBOL_NAME(sys_setpriority)
	.long SYMBOL_NAME(sys_profil)
	.long SYMBOL_NAME(sys_statfs)
	.long SYMBOL_NAME(sys_fstatfs)		/* 100 */
	.long SYMBOL_NAME(sys_ioperm)
	.long SYMBOL_NAME(sys_socketcall)
	.long SYMBOL_NAME(sys_syslog)
	.long SYMBOL_NAME(sys_setitimer)
	.long SYMBOL_NAME(sys_getitimer)	/* 105 */
	.long SYMBOL_NAME(sys_newstat)
	.long SYMBOL_NAME(sys_newlstat)
	.long SYMBOL_NAME(sys_newfstat)
	.long SYMBOL_NAME(sys_uname)
	.long SYMBOL_NAME(sys_ni_syscall)	/* iopl for i386 */ /* 110 */
	.long SYMBOL_NAME(sys_vhangup)
	.long SYMBOL_NAME(sys_idle)
	.long SYMBOL_NAME(sys_ni_syscall)	/* vm86 for i386 */
	.long SYMBOL_NAME(sys_wait4)
	.long SYMBOL_NAME(sys_swapoff)		/* 115 */
	.long SYMBOL_NAME(sys_sysinfo)
	.long SYMBOL_NAME(sys_ipc)
	.long SYMBOL_NAME(sys_fsync)
	.long SYMBOL_NAME(sys_sigreturn)
	.long SYMBOL_NAME(sys_clone)		/* 120 */
	.long SYMBOL_NAME(sys_setdomainname)
	.long SYMBOL_NAME(sys_newuname)
	.long SYMBOL_NAME(sys_ni_syscall)	/* modify_ldt for i386 */
	.long SYMBOL_NAME(sys_adjtimex)
	.long SYMBOL_NAME(sys_mprotect)		/* 125 */
	.long SYMBOL_NAME(sys_sigprocmask)
	.long SYMBOL_NAME(sys_create_module)
	.long SYMBOL_NAME(sys_init_module)
	.long SYMBOL_NAME(sys_delete_module)
	.long SYMBOL_NAME(sys_get_kernel_syms)	/* 130 */
	.long SYMBOL_NAME(sys_quotactl)
	.long SYMBOL_NAME(sys_getpgid)
	.long SYMBOL_NAME(sys_fchdir)
	.long SYMBOL_NAME(sys_bdflush)
	.long SYMBOL_NAME(sys_sysfs)		/* 135 */
	.long SYMBOL_NAME(sys_personality)
	.long SYMBOL_NAME(sys_ni_syscall)	/* for afs_syscall */
	.long SYMBOL_NAME(sys_setfsuid)
	.long SYMBOL_NAME(sys_setfsgid)
	.long SYMBOL_NAME(sys_llseek)		/* 140 */
	.long SYMBOL_NAME(sys_getdents)
	.long SYMBOL_NAME(sys_select)
	.long SYMBOL_NAME(sys_flock)
	.long SYMBOL_NAME(sys_msync)
	.long SYMBOL_NAME(sys_readv)		/* 145 */
	.long SYMBOL_NAME(sys_writev)
	.long SYMBOL_NAME(sys_getsid)
	.long SYMBOL_NAME(sys_fdatasync)
	.long SYMBOL_NAME(sys_sysctl)
	.long SYMBOL_NAME(sys_mlock)		/* 150 */
	.long SYMBOL_NAME(sys_munlock)
	.long SYMBOL_NAME(sys_mlockall)
	.long SYMBOL_NAME(sys_munlockall)
	.long SYMBOL_NAME(sys_sched_setparam)
	.long SYMBOL_NAME(sys_sched_getparam)   /* 155 */
	.long SYMBOL_NAME(sys_sched_setscheduler)
	.long SYMBOL_NAME(sys_sched_getscheduler)
	.long SYMBOL_NAME(sys_sched_yield)
	.long SYMBOL_NAME(sys_sched_get_priority_max)
	.long SYMBOL_NAME(sys_sched_get_priority_min)  /* 160 */
	.long SYMBOL_NAME(sys_sched_rr_get_interval)
	.long SYMBOL_NAME(sys_nanosleep)
	.long SYMBOL_NAME(sys_mremap)
	.space (NR_syscalls-163)*4
	
