/*
 *  linux/arch/m68k/kernel/entry.S
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file README.legal in the main directory of this archive
 * for more details.
 *
 * 680x0 support by Hamish Macdonald
 *
 */

/*
 * entry.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_exception':
 *	ptrace needs to have all regs on the stack.
 *	if the order here is changed, it needs to be
 *	updated in fork.c:sys_fork, signal.c:do_signal,
 *	ptrace.c and ptrace.h
 *
 *	This allows access to the syscall arguments in registers d1-d5
 *
 *	 0(sp) - d1
 *	 4(sp) - d2
 *	 8(sp) - d3
 *	 C(sp) - d4
 *	10(sp) - d5
 *	14(sp) - d6
 *	18(sp) - d7
 *	1C(sp) - a0
 *	20(sp) - a1
 *	24(sp) - a2
 *	28(sp) - a3
 *	2C(sp) - a4
 *	30(sp) - a5
 *	34(sp) - a6
 *	38(sp) - d0
 *	3C(sp) - usp
 *	40(sp) - orig_d0
 *	44(sp) - stack adjustment
 *	46(sp) - sr
 *	48(sp) - pc
 *	4C(sp) - format & vector
 */

#include <linux/sys.h>

#include <asm/segment.h>

LOFF_D1 	= 0x00
LOFF_D2 	= 0x04
LOFF_D3 	= 0x08
LOFF_D4 	= 0x0C
LOFF_D5 	= 0x10
LOFF_D6 	= 0x14
LOFF_D7 	= 0x18
LOFF_A0 	= 0x1C
LOFF_A1 	= 0x20
LOFF_A2 	= 0x24
LOFF_A3 	= 0x28
LOFF_A4 	= 0x2C
LOFF_A5 	= 0x30
LOFF_A6 	= 0x34
LOFF_D0 	= 0x38
LOFF_USP	= 0x3C
LOFF_ORIG_D0	= 0x40
LOFF_STKADJ	= 0x44
LOFF_SR 	= 0x46
LOFF_PC 	= 0x48
LOFF_FORMAT	= 0x4C


LCF_MASK	= 0x0001

LENOSYS = 38

/*
 * these are offsets into the task-struct
 */
LTS_STATE	=  0
LTS_COUNTER	=  4
LTS_PRIORITY	=  8
LTS_SIGNAL	= 12
LTS_BLOCKED	= 16
LTS_FLAGS	= 20
LTS_ERRNO	= 24

#include <linux/autoconf.h>
#include <linux/linkage.h>

/* the following macro is used when enabling interrupts */
#if defined(CONFIG_ATARI) && !defined(CONFIG_AMIGA) && !defined(CONFIG_MAC)
	/* block out HSYNC on the atari */
#define ALLOWINT 0xfbff
#else
	/* portable version */
#define ALLOWINT 0xf8ff
#endif /* machine compilation types */ 

.globl SYMBOL_NAME(system_call), SYMBOL_NAME(buserr), SYMBOL_NAME(trap)
.globl SYMBOL_NAME(ret_from_exception), SYMBOL_NAME(inthandler)
.globl SYMBOL_NAME(sys_call_table), SYMBOL_NAME(ret_from_interrupt)
.globl SYMBOL_NAME(bad_interrupt)

.text
ENTRY(buserr)
	clrw	%sp@-			| stack adjustment
	subql	#8,%sp			| make room for usp and orig_d0
	movel	%d0,%sp@-
	moveml	%d1-%a6,%sp@-
	move	%usp,%a0
	movel	%a0,%sp@(LOFF_USP)
	moveq	#-1,%d0
	movel	%d0,%sp@(LOFF_ORIG_D0)	| a -1 in the ORIG_D0 field
					| signifies that the stack frame
					| is NOT for syscall

	movel	%sp,%sp@- 		| stack frame pointer argument
	bsrl	SYMBOL_NAME(buserr_c)
	addql	#4,%sp
	jra	SYMBOL_NAME(ret_from_exception)

ENTRY(trap)
	clrw	%sp@-			| stack adjustment
	subql	#8,%sp			| make room for usp and orig_d0
	movel	%d0,%sp@-
	moveml	%d1-%a6,%sp@-
	move	%usp,%a0
	movel	%a0,%sp@(LOFF_USP)
	moveq	#-1,%d0
	movel	%d0,%sp@(LOFF_ORIG_D0)	| a -1 in the ORIG_D0 field
					| signifies that the stack frame
					| is NOT for syscall
	movel	%sp,%sp@- 		| stack frame pointer argument
	bsrl	SYMBOL_NAME(trap_c)
	addql	#4,%sp
	jra	SYMBOL_NAME(ret_from_exception)

	ALIGN
reschedule:
	pea	SYMBOL_NAME(ret_from_exception)
	jmp	SYMBOL_NAME(schedule)

ENTRY(system_call)
	clrw	%sp@-			| stack adjustment
	subql	#8,%sp			| make room for usp and orig_d0
	pea	(-LENOSYS):w		| default return value in D0
	moveml	%d1-%a6,%sp@-		| save registers
	move	%usp,%a0
	movel	%a0,%sp@(LOFF_USP)	| save User Stack Pointer
	movel	%d0,%sp@(LOFF_ORIG_D0)	| save original D0 (syscall #)
	movel	%d0,%d2

	| save top of frame
	pea	%sp@
	jbsr	SYMBOL_NAME(set_esp0)
	addql	#4,%sp

	cmpl	#NR_syscalls,%d2
	jcc	SYMBOL_NAME(ret_from_exception)
	lea	SYMBOL_NAME(sys_call_table),%a2
	movel	%a2@(%d2:l:4),%a2
	tstl	%a2
	jeq	SYMBOL_NAME(ret_from_exception)
	andw	#~LCF_MASK,%sp@(LOFF_SR) | assume syscall success
	movel	SYMBOL_NAME(current),%a3
	clrl	%a3@(LTS_ERRNO)
	btst	#5,%a3@(LTS_FLAGS+3)	| PF_TRACESYS
	bnes	1f
	jbsr	%a2@
	movel	%d0,%sp@(LOFF_D0)	| save the return value
	jpl	2f
	orw	#LCF_MASK,%sp@(LOFF_SR)	| set carry to indicate error
2:
	movel	%a3@(LTS_ERRNO),%d1
	negl	%d1
	jeq	SYMBOL_NAME(ret_from_exception)
	movel	%d1,%sp@(LOFF_D0)
	orw	#LCF_MASK,%sp@(LOFF_SR)	| set carry to indicate error
	jra	SYMBOL_NAME(ret_from_exception)
1:	jbsr	SYMBOL_NAME(syscall_trace)
	jbsr	%a2@
	movel	%d0,%sp@(LOFF_D0)	| save the return value
	jpl	2f
	orw	#LCF_MASK,%sp@(LOFF_SR)	| set carry to indicate error
2:
	movel	%a3@(LTS_ERRNO),%d1
	negl	%d1
	jeq	2f
	movel	%d1,%sp@(LOFF_D0)
	orw	#LCF_MASK,%sp@(LOFF_SR)	| set carry to indicate error
2:	jbsr	SYMBOL_NAME(syscall_trace)

SYMBOL_NAME_LABEL(ret_from_exception)
	btst	#5,%sp@(LOFF_SR)	| check if returning to kernel
	bnes	2f			| if so, skip resched, signals
	tstl	SYMBOL_NAME(need_resched)
	jne	reschedule
	movel	SYMBOL_NAME(current),%a2
	cmpl	#SYMBOL_NAME(task),%a2	| task[0] cannot have signals
	jeq	2f
	bclr	#6,%a2@(LTS_FLAGS+3)	| check for delayed trace
	jeq	1f
	bclr	#7,%sp@(LOFF_SR)	| clear trace bit in SR
	pea	1			| send SIGTRAP
	movel	%a2,%sp@-
	pea	5
	jbsr	SYMBOL_NAME(send_sig)
	addql	#8,%sp
	addql	#4,%sp
1:
	tstl	%a2@(LTS_STATE)		| state
	jne	reschedule
	tstl	%a2@(LTS_COUNTER)	| counter
	jeq	reschedule
	movel	%a2@(LTS_BLOCKED),%d0
	movel	%d0,%d1			| save blocked in d1 for signal handling
	notl	%d0
	andl	%a2@(LTS_SIGNAL),%d0
	jne	Lsignal_return
2:	movel	%sp@(LOFF_USP),%a0
	move	%a0,%usp
	moveml	%sp@+,%d1-%a6
	movel	%sp@+,%d0 		| restore d0
	addql	#8,%sp			| skip the usp and orig_d0
	addw	%sp@+,%sp 		| add the stack adjustment
	rte
Lsignal_return:
	movel	%sp,%sp@-
	movel	%d1,%sp@-
	bsrl	SYMBOL_NAME(do_signal)
	addql	#8,%sp
	bras	2b


/*
** This is the main interrupt handler, responsible for calling process_int()
** or (*mach_process_int)(), if it is a non-autovector int.
*/
SYMBOL_NAME_LABEL(inthandler)
	moveml	%a0-%a1/%d0-%d1,%sp@-	|  save registers

	addql	#1,SYMBOL_NAME(intr_count)
	movew	%sp@(22),%d0		|  put exception # in d0
	andil	#0xfff,%d0		|  mask out format nybble

	cmpw	#0x100,%d0		|  check if user interrupt
	jge	Luserint

	subiw	#0x60,%d0		|  convert from vector offset...
	lsrl	#2,%d0			|  ...to IRQ number

	jeq	Lspurious		|  if zero, spurious interrupt
#ifdef MASTER_STACK
	movec	%msp,%a0		|  push interrupt stack frame address
	movel	%a0,%sp@-
#else
	pea	%sp@(16)
#endif
	movel	%d0,%sp@- 		|  put IRQ # on stack
	jbsr	SYMBOL_NAME(process_int) |  process the IRQ
	addql	#8,%sp			|  pop parameters off stack
	jra 	1f
Luserint:
	movel	SYMBOL_NAME(mach_process_int),%a1
	tstl	%a1			| if no hook installed
	jeq	Lspurious		| ... then spurious interrupt

	subiw	#0x100,%d0		| convert from vector offset...
	lsrl	#2,%d0			| ...to IRQ number
#ifdef MASTER_STACK
	movec	%msp,%a0		|  push interrupt stack frame address
	movel	%a0,%sp@-
#else
	pea	%sp@(16)
#endif
	movel	%d0,%sp@-		| put IRQ # on stack
	jsr	%a1@			| process the IRQ
	addql	#8,%sp			| pop parameters off stack

1:
SYMBOL_NAME_LABEL(ret_from_interrupt)

	/* maybe process software interrupts */
	subql	#1,SYMBOL_NAME(intr_count)
	jne	1f

2:	movel	SYMBOL_NAME(bh_mask),%d0
	andl	SYMBOL_NAME(bh_active),%d0
	jeq	1f

	movew	%sr,%sp@-
	addql	#1,SYMBOL_NAME(intr_count)
	andiw	#(ALLOWINT),%sr		| allow interrupts
	jbsr	SYMBOL_NAME(do_bottom_half)
	movew	%sp@+,%sr
	subql	#1,SYMBOL_NAME(intr_count)
	/* ++roman: re-check for bottom halfs newly marked... */
	bras	2b
1:
	/*
	 * check if returning to user mode.
	 * if so, pop stuff off interrupt stack, switch
	 * from interrupt mode to master mode, push
	 * stack frame on kernel stack and jump to ret_from_exception
	 */
#ifdef MASTER_STACK
	moveb	%sp@(22),%d0		| get format word from int stack
	andb	#0xf0,%d0		| mask off format
	cmpb	#0x10,%d0		| check if throwaway frame
	jeq	Lnotstacked		| if, not: interrupts stacked, rte
	moveml	%sp@+,%a0-%a1/%d0-%d1	| restore registers
	rte
Lnotstacked:
	movec	%msp,%a0		| get the master stack pointer
	btst	#0x5,%a0@		| supervisor bit set in saved SR?
	jeq	Lnotkernel		| if in kernel, rte
#else
	btst	#0x5,%sp@(16)		| supervisor bit set in saved SR?
	jeq	Lnotkernel		| if in kernel, rte
#endif
	moveml	%sp@+,%a0-%a1/%d0-%d1	| restore registers
	rte
Lnotkernel:
	/* Avoid building syscall frame if we don't need to.
	   ret_from_syscall will do the same tests again, but most of
	   the time it will return right away and building and
	   restoring the syscall frame takes more time.  */

	tstl	SYMBOL_NAME(need_resched)
	jne	Lbuildframe
	/* No need to check for delayed trace, cannot happen.  */
	movel	SYMBOL_NAME(current),%a0
	tstl	%a0@(LTS_STATE)
	jne	Lbuildframe
	tstl	%a0@(LTS_COUNTER)
	jeq	Lbuildframe
	movel	%a0@(LTS_BLOCKED),%d0
	notl	%d0
	andl	%a0@(LTS_SIGNAL),%d0
	jne	Lbuildframe

	/* Nothing to do for this process, return now.  */
	moveml	%sp@+,%a0-%a1/%d0-%d1	| restore registers
	rte

Lbuildframe:
	moveml	%sp@+,%a0-%a1/%d0-%d1	| restore registers
#ifdef MASTER_STACK
	addql	#8,%sp			| throw away throwaway stack frame
	oriw	#0x1000,%sr		| set master bit in SR (sp is now msp)
#endif
	andiw	#(ALLOWINT),%sr		| allow interrupts
	clrw	%sp@-			| stack adjustment
	subql	#8,%sp			| make room for usp and orig_d0
	movel	%d0,%sp@- 		| save registers as for syscall
	moveml	%d1-%a6,%sp@-
	moveq	#-1,%d0
	movel	%d0,%sp@(LOFF_ORIG_D0)	| indicate stack frame not for syscall
	move	%usp,%a0
	movel	%a0,%sp@(LOFF_USP)	| save usp

	| save top of frame
	pea	%sp@
	jbsr	SYMBOL_NAME(set_esp0)
	addql	#4,%sp

	jra	SYMBOL_NAME(ret_from_exception)	| deliver signals, reschedule etc..
Lspurious:
	addql	#1,SYMBOL_NAME(num_spurious)	| increment the counter of spurious
					| interrupts.
	subql	#1,SYMBOL_NAME(intr_count)
	moveml	%sp@+,%a0-%a1/%d0-%d1	| restore registers
	rte				| return from exception


/* Handler for uninitialized and spurious interrupts */

SYMBOL_NAME_LABEL(bad_interrupt)
	addql	#1,SYMBOL_NAME(num_spurious)
	rte


.data
ALIGN
SYMBOL_NAME_LABEL(sys_call_table)
	.long SYMBOL_NAME(sys_setup)		/* 0 */
	.long SYMBOL_NAME(sys_exit)
	.long SYMBOL_NAME(sys_fork)
	.long SYMBOL_NAME(sys_read)
	.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(sys_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(sys_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_ni_syscall) /* ioperm for i386 */
	.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_cacheflush)	/* 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 0				/* for afs_syscall */
	.long SYMBOL_NAME(sys_setfsuid)
	.long SYMBOL_NAME(sys_setfsgid)
	.long SYMBOL_NAME(sys_llseek)		/* 140 */
	.long SYMBOL_NAME(sys_getdents)
	.space (NR_syscalls-141)*4
