/*
 *  arch/s390/kernel/entry.S
 *    S390 low-level entry points.
 *
 *  S390 version
 *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
 *               Hartmut Penner (hp@de.ibm.com),
 *               Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
 */

#include <linux/sys.h>
#include <linux/linkage.h>
#include <linux/config.h>
#include <asm/cache.h>
#include <asm/lowcore.h>
#include <asm/errno.h>
#include <asm/smp.h>
#include <asm/ptrace.h>

#include "asm-offsets.h"

/*
 * Stack layout for the system_call stack entry.
 * The first few entries are identical to the user_regs_struct.
 */
SP_PTREGS    =  STACK_FRAME_OVERHEAD 
SP_PSW       =  STACK_FRAME_OVERHEAD + PT_PSWMASK
SP_R0        =  STACK_FRAME_OVERHEAD + PT_GPR0
SP_R1        =  STACK_FRAME_OVERHEAD + PT_GPR1
SP_R2        =  STACK_FRAME_OVERHEAD + PT_GPR2
SP_R3        =  STACK_FRAME_OVERHEAD + PT_GPR3
SP_R4        =  STACK_FRAME_OVERHEAD + PT_GPR4
SP_R5        =  STACK_FRAME_OVERHEAD + PT_GPR5
SP_R6        =  STACK_FRAME_OVERHEAD + PT_GPR6
SP_R7        =  STACK_FRAME_OVERHEAD + PT_GPR7
SP_R8        =  STACK_FRAME_OVERHEAD + PT_GPR8
SP_R9        =  STACK_FRAME_OVERHEAD + PT_GPR9
SP_R10       =  STACK_FRAME_OVERHEAD + PT_GPR10
SP_R11       =  STACK_FRAME_OVERHEAD + PT_GPR11
SP_R12       =  STACK_FRAME_OVERHEAD + PT_GPR12
SP_R13       =  STACK_FRAME_OVERHEAD + PT_GPR13
SP_R14       =  STACK_FRAME_OVERHEAD + PT_GPR14
SP_R15       =  STACK_FRAME_OVERHEAD + PT_GPR15
SP_AREGS     =  STACK_FRAME_OVERHEAD + PT_ACR0
SP_ORIG_R2   =  STACK_FRAME_OVERHEAD + PT_ORIGGPR2
/* Now the additional entries */
SP_TRAP      =  (SP_ORIG_R2+GPR_SIZE)
SP_SIZE      =  (SP_TRAP+4)

/*
 * Base Address of this Module --- saved in __LC_ENTRY_BASE
 */
       .globl entry_base
entry_base:

#define BASED(name) name-entry_base(%r13)

/*
 * Register usage in interrupt handlers:
 *    R9  - pointer to current task structure
 *    R13 - pointer to literal pool
 *    R14 - return register for function calls
 *    R15 - kernel stack pointer
 */

        .macro  SAVE_ALL_BASE
        stm     %r13,%r15,__LC_SAVE_AREA
        basr    %r13,0                    #  temp base pointer
0:	stam    %a2,%a4,__LC_SAVE_AREA+12
        l       %r13,.Lentry_base-0b(%r13)# load &entry_base to %r13
	.endm

        .macro  SAVE_ALL psworg,sync      # system entry macro
        tm      \psworg+1,0x01            # test problem state bit
	.if	\sync
        bz      BASED(1f)                 # skip stack setup save
	.else
        bnz     BASED(0f)                 # from user -> load kernel stack
	l	%r14,__LC_ASYNC_STACK	  # are we already on the async stack ?
	slr     %r14,%r15
	sra	%r14,13
	be	BASED(1f)
        l       %r15,__LC_ASYNC_STACK     # load async. stack
	b	BASED(1f)
	.endif
0:      l       %r15,__LC_KERNEL_STACK    # problem state -> load ksp
	lam	%a2,%a4,BASED(.Lc_ac)	  # set ac.reg. 2 to primary space
					  # and ac.reg. 4 to home space
1:      s       %r15,BASED(.Lc_spsize)    # make room for registers & psw
        n       %r15,BASED(.Lc0xfffffff8) # align stack pointer to 8
        stm     %r0,%r12,SP_R0(%r15)      # store gprs 0-12 to kernel stack
        st      %r2,SP_ORIG_R2(%r15)      # store original content of gpr 2
        mvc     SP_R13(12,%r15),__LC_SAVE_AREA  # move R13-R15 to stack
        stam    %a0,%a15,SP_AREGS(%r15)   # store access registers to kst.
        mvc     SP_AREGS+8(12,%r15),__LC_SAVE_AREA+12 # store ac. regs
        mvc     SP_PSW(8,%r15),\psworg    # move user PSW to stack
        la      %r0,\psworg               # store trap indication
        st      %r0,SP_TRAP(%r15)
        xc      0(4,%r15),0(%r15)         # clear back chain
        .endm

        .macro  RESTORE_ALL sync          # system exit macro
        mvc     __LC_RETURN_PSW(8),SP_PSW(%r15)  # move user PSW to lowcore
        lam     %a0,%a15,SP_AREGS(%r15)   # load the access registers
        lm      %r0,%r15,SP_R0(%r15)      # load gprs 0-15 of user
        ni      __LC_RETURN_PSW+1,0xfd    # clear wait state bit
        lpsw    __LC_RETURN_PSW           # back to caller
        .endm

        .macro  GET_CURRENT
	l	%r9,BASED(.Lc0xffffe000)  # load pointer to task_struct to %r9
	al	%r9,__LC_KERNEL_STACK
        .endm


/*
 * Scheduler resume function, called by switch_to
 *  gpr2 = (task_struct *) prev
 *  gpr3 = (task_struct *) next
 * Returns:
 *  gpr2 = prev
 */
        .globl  resume
resume:
        basr    %r1,0
resume_base:
	tm	__THREAD_per(%r3),0xe8		# new process is using per ?
	bz	resume_noper-resume_base(%r1)	# if not we're fine
        stctl   %c9,%c11,24(%r15)		# We are using per stuff
        clc     __THREAD_per(12,%r3),24(%r15)
        be      resume_noper-resume_base(%r1)	# we got away w/o bashing TLB's
        lctl    %c9,%c11,__THREAD_per(%r3)	# Nope we didn't
resume_noper:
        stm     %r6,%r15,24(%r15)       # store resume registers of prev task
	st	%r15,__THREAD_ksp(%r2)	# store kernel stack to prev->tss.ksp
	l	%r15,__THREAD_ksp(%r3)	# load kernel stack from next->tss.ksp
	stam    %a2,%a2,__THREAD_ar2(%r2)	# store kernel access reg. 2
	stam    %a4,%a4,__THREAD_ar4(%r2)	# store kernel access reg. 4
	lam     %a2,%a2,__THREAD_ar2(%r3)	# load kernel access reg. 2
	lam     %a4,%a4,__THREAD_ar4(%r3)	# load kernel access reg. 4
	lm	%r6,%r15,24(%r15)	# load resume registers of next task
	ahi	%r3,8192
	st	%r3,__LC_KERNEL_STACK	# __LC_KERNEL_STACK = new kernel stack
	br	%r14

/*
 * do_softirq calling function. We want to run the softirq functions on the
 * asynchronous interrupt stack.
 */
	.global do_call_softirq
do_call_softirq:
	stm	%r12,%r15,24(%r15)
	lr	%r12,%r15
        basr    %r13,0
do_call_base:
	l	%r0,__LC_ASYNC_STACK
	slr     %r0,%r15
	sra	%r0,13
	be	0f-do_call_base(%r13)
	l	%r15,__LC_ASYNC_STACK
0:	sl	%r15,.Lc_overhead-do_call_base(%r13)
        st	%r12,0(%r15)	# store backchain
	l	%r1,.Ldo_softirq-do_call_base(%r13)
	basr	%r14,%r1
	lm	%r12,%r15,24(%r12)
	br	%r14
	
/*
 * SVC interrupt handler routine. System calls are synchronous events and
 * are executed with interrupts enabled.
 */

	.globl  system_call
system_call:
	SAVE_ALL_BASE
        SAVE_ALL __LC_SVC_OLD_PSW,1
	lh	%r8,0x8a	  # get svc number from lowcore
        sll     %r8,2
        GET_CURRENT               # load pointer to task_struct to R9
        stosm   24(%r15),0x03     # reenable interrupts
        l       %r8,sys_call_table-entry_base(%r8,%r13) # get system call addr.
        tm      __TASK_ptrace+3(%r9),0x02 # PT_TRACESYS
        bnz     BASED(sysc_tracesys)
        basr    %r14,%r8          # call sys_xxxx
        st      %r2,SP_R2(%r15)   # store return value (change R2 on stack)
                                  # ATTENTION: check sys_execve_glue before
                                  # changing anything here !!

sysc_return:
        tm      SP_PSW+1(%r15),0x01 # returning to user ?
        bno     BASED(sysc_leave) # no-> skip resched & signal
#
# check, if reschedule is needed
#
        icm     %r0,15,__TASK_need_resched(%r9)
        bnz     BASED(sysc_reschedule)
        icm     %r0,15,__TASK_sigpending(%r9)
        bnz     BASED(sysc_signal_return)
sysc_leave:
	stnsm   24(%r15),0xfc     # disable I/O and ext. interrupts
        RESTORE_ALL 1

#
# call do_signal before return
#
sysc_signal_return:     
        la      %r2,SP_PTREGS(%r15) # load pt_regs
        sr      %r3,%r3           # clear *oldset
        l       %r1,BASED(.Ldo_signal)
	la      %r14,BASED(sysc_leave)
        br      %r1               # return point is sysc_leave
#
# call schedule with sysc_return as return-address
#
sysc_reschedule:        
        l       %r1,BASED(.Lschedule)
	la      %r14,BASED(sysc_return)
        br      %r1               # call scheduler, return to sysc_return
#
# call trace before and after sys_call
#
sysc_tracesys:
	la	%r11,BASED(sysc_return)

#
# call syscall_trace before and after system call
# special linkage: %r11 contains the return address for trace_svc
#
trace_svc:
        l       %r1,BASED(.Ltrace)
	l       %r7,BASED(.Lc_ENOSYS)
	st      %r7,SP_R2(%r15)   # give sysc_trace an -ENOSYS retval
        basr    %r14,%r1
	l       %r2,SP_R2(%r15)
	cr      %r2,%r7		  # compare with saved -ENOSYS
	be      BASED(trace_svc_go) # strace changed the syscall ?
	sll     %r2,24
	srl     %r2,22
        l       %r8,sys_call_table-entry_base(%r2,%r13) # get system call addr.
trace_svc_go:
	lm      %r3,%r6,SP_R3(%r15)
	l       %r2,SP_ORIG_R2(%r15)
        basr    %r14,%r8          # call sys_xxx
        st      %r2,SP_R2(%r15)   # store return value
        l       %r1,BASED(.Ltrace)
	lr	%r14,%r11	  # return point is in %r11
        br      %r1

#
# a new process exits the kernel with ret_from_fork
#
        .globl  ret_from_fork
ret_from_fork:  
        basr    %r13,0
        l       %r13,.Lentry_base-.(%r13)  # setup base pointer to &entry_base
        GET_CURRENT               # load pointer to task_struct to R9
        stosm   24(%r15),0x03     # reenable interrupts
        sr      %r0,%r0           # child returns 0
        st      %r0,SP_R2(%r15)   # store return value (change R2 on stack)
        l       %r1,BASED(.Lschedtail)
	la      %r14,BASED(sysc_return)
        br      %r1               # call schedule_tail, return to sysc_return

#
# clone, fork, vfork, exec and sigreturn need glue,
# because they all expect pt_regs as parameter,
# but are called with different parameter.
# return-address is set up above
#
sys_clone_glue: 
        la      %r2,SP_PTREGS(%r15)    # load pt_regs
        l       %r1,BASED(.Lclone)
        br      %r1                   # branch to sys_clone

sys_fork_glue:  
        la      %r2,SP_PTREGS(%r15)    # load pt_regs
        l       %r1,BASED(.Lfork)
        br      %r1                   # branch to sys_fork

sys_vfork_glue: 
        la      %r2,SP_PTREGS(%r15)    # load pt_regs
        l       %r1,BASED(.Lvfork)
        br      %r1                   # branch to sys_vfork

sys_execve_glue:        
        la      %r2,SP_PTREGS(%r15)   # load pt_regs
        l       %r1,BASED(.Lexecve)
	lr      %r12,%r14             # save return address
        basr    %r14,%r1              # call sys_execve
        ltr     %r2,%r2               # check if execve failed
        bnz     0(%r12)               # it did fail -> store result in gpr2
        b       4(%r12)               # SKIP ST 2,SP_R2(15) after BASR 14,8
                                      # in system_call/sysc_tracesys

sys_sigreturn_glue:     
        la      %r2,SP_PTREGS(%r15)   # load pt_regs as parameter
        l       %r1,BASED(.Lsigreturn)
        br      %r1                   # branch to sys_sigreturn

sys_rt_sigreturn_glue:     
        la      %r2,SP_PTREGS(%r15)   # load pt_regs as parameter
        l       %r1,BASED(.Lrt_sigreturn)
        br      %r1                   # branch to sys_sigreturn

#
# sigsuspend and rt_sigsuspend need pt_regs as an additional
# parameter and they have to skip the store of %r2 into the
# user register %r2 because the return value was set in 
# sigsuspend and rt_sigsuspend already and must not be overwritten!
#

sys_sigsuspend_glue:    
        lr      %r5,%r4               # move mask back
        lr      %r4,%r3               # move history1 parameter
        lr      %r3,%r2               # move history0 parameter
        la      %r2,SP_PTREGS(%r15)   # load pt_regs as first parameter
        l       %r1,BASED(.Lsigsuspend)
	la      %r14,4(%r14)          # skip store of return value
        br      %r1                   # branch to sys_sigsuspend

sys_rt_sigsuspend_glue: 
        lr      %r4,%r3               # move sigsetsize parameter
        lr      %r3,%r2               # move unewset parameter
        la      %r2,SP_PTREGS(%r15)   # load pt_regs as first parameter
        l       %r1,BASED(.Lrt_sigsuspend)
	la      %r14,4(%r14)          # skip store of return value
        br      %r1                   # branch to sys_rt_sigsuspend

sys_sigaltstack_glue:
        la      %r4,SP_PTREGS(%r15)   # load pt_regs as parameter
        l       %r1,BASED(.Lsigaltstack)
        br      %r1                   # branch to sys_sigreturn

	.globl  sys_call_table	
sys_call_table:
        .long  sys_ni_syscall            /* 0 */
        .long  sys_exit
        .long  sys_fork_glue
        .long  sys_read
        .long  sys_write
        .long  sys_open                  /* 5 */
        .long  sys_close
        .long  sys_ni_syscall           /* old waitpid syscall holder */
        .long  sys_creat
        .long  sys_link
        .long  sys_unlink                /* 10 */
        .long  sys_execve_glue
        .long  sys_chdir
        .long  sys_time
        .long  sys_mknod
        .long  sys_chmod                /* 15 */
        .long  sys_lchown16
        .long  sys_ni_syscall           /* old break syscall holder */
        .long  sys_ni_syscall           /* old stat syscall holder */
        .long  sys_lseek
        .long  sys_getpid               /* 20 */
        .long  sys_mount
        .long  sys_oldumount
        .long  sys_setuid16
        .long  sys_getuid16
        .long  sys_stime                /* 25 */
        .long  sys_ptrace
        .long  sys_alarm
        .long  sys_ni_syscall           /* old fstat syscall holder */
        .long  sys_pause
        .long  sys_utime                /* 30 */
        .long  sys_ni_syscall           /* old stty syscall holder */
        .long  sys_ni_syscall           /* old gtty syscall holder */
        .long  sys_access
        .long  sys_nice
        .long  sys_ni_syscall           /* 35 */  /* old ftime syscall holder */
        .long  sys_sync
        .long  sys_kill
        .long  sys_rename
        .long  sys_mkdir
        .long  sys_rmdir                /* 40 */
        .long  sys_dup
        .long  sys_pipe
        .long  sys_times
        .long  sys_ni_syscall           /* old prof syscall holder */
        .long  sys_brk                  /* 45 */
        .long  sys_setgid16
        .long  sys_getgid16
        .long  sys_signal
        .long  sys_geteuid16
        .long  sys_getegid16            /* 50 */
        .long  sys_acct
        .long  sys_umount
        .long  sys_ni_syscall           /* old lock syscall holder */
        .long  sys_ioctl
        .long  sys_fcntl                /* 55 */
        .long  sys_ni_syscall           /* old mpx syscall holder */
        .long  sys_setpgid
        .long  sys_ni_syscall           /* old ulimit syscall holder */
        .long  sys_ni_syscall           /* old uname syscall holder */
        .long  sys_umask                /* 60 */
        .long  sys_chroot
        .long  sys_ustat
        .long  sys_dup2
        .long  sys_getppid
        .long  sys_getpgrp              /* 65 */
        .long  sys_setsid
        .long  sys_sigaction
        .long  sys_ni_syscall           /* old sgetmask syscall holder */
        .long  sys_ni_syscall           /* old ssetmask syscall holder */
        .long  sys_setreuid16           /* 70 */
        .long  sys_setregid16
        .long  sys_sigsuspend_glue
        .long  sys_sigpending
        .long  sys_sethostname
        .long  sys_setrlimit            /* 75 */
        .long  sys_old_getrlimit
        .long  sys_getrusage
        .long  sys_gettimeofday
        .long  sys_settimeofday
        .long  sys_getgroups16          /* 80 */
        .long  sys_setgroups16
        .long  sys_ni_syscall           /* old select syscall holder */
        .long  sys_symlink
        .long  sys_ni_syscall           /* old lstat syscall holder */
        .long  sys_readlink             /* 85 */
        .long  sys_uselib
        .long  sys_swapon
        .long  sys_reboot
        .long  sys_ni_syscall           /* old readdir syscall holder */
        .long  old_mmap                 /* 90 */
        .long  sys_munmap
        .long  sys_truncate
        .long  sys_ftruncate
        .long  sys_fchmod
        .long  sys_fchown16              /* 95 */
        .long  sys_getpriority
        .long  sys_setpriority
        .long  sys_ni_syscall            /* old profil syscall holder */
        .long  sys_statfs
        .long  sys_fstatfs               /* 100 */
        .long  sys_ioperm
        .long  sys_socketcall
        .long  sys_syslog
        .long  sys_setitimer
        .long  sys_getitimer             /* 105 */
        .long  sys_newstat
        .long  sys_newlstat
        .long  sys_newfstat
        .long  sys_ni_syscall            /* old uname syscall holder */
        .long  sys_ni_syscall            /* 110 */ /* iopl for i386 */
        .long  sys_vhangup
        .long  sys_ni_syscall            /* old "idle" system call */
        .long  sys_ni_syscall            /* vm86old for i386 */
        .long  sys_wait4
        .long  sys_swapoff               /* 115 */
        .long  sys_sysinfo
        .long  sys_ipc
        .long  sys_fsync
        .long  sys_sigreturn_glue
        .long  sys_clone_glue            /* 120 */
        .long  sys_setdomainname
        .long  sys_newuname
        .long  sys_ni_syscall            /* modify_ldt for i386 */
        .long  sys_adjtimex
        .long  sys_mprotect              /* 125 */
        .long  sys_sigprocmask
        .long  sys_create_module
        .long  sys_init_module
        .long  sys_delete_module
        .long  sys_get_kernel_syms       /* 130 */
        .long  sys_quotactl
        .long  sys_getpgid
        .long  sys_fchdir
        .long  sys_bdflush
        .long  sys_sysfs                 /* 135 */
        .long  sys_personality
        .long  sys_ni_syscall            /* for afs_syscall */
        .long  sys_setfsuid16
        .long  sys_setfsgid16
        .long  sys_llseek                /* 140 */
        .long  sys_getdents
        .long  sys_select
        .long  sys_flock
        .long  sys_msync
        .long  sys_readv                 /* 145 */
        .long  sys_writev
        .long  sys_getsid
        .long  sys_fdatasync
        .long  sys_sysctl
        .long  sys_mlock                 /* 150 */
        .long  sys_munlock
        .long  sys_mlockall
        .long  sys_munlockall
        .long  sys_sched_setparam
        .long  sys_sched_getparam        /* 155 */
        .long  sys_sched_setscheduler
        .long  sys_sched_getscheduler
        .long  sys_sched_yield
        .long  sys_sched_get_priority_max
        .long  sys_sched_get_priority_min  /* 160 */
        .long  sys_sched_rr_get_interval
        .long  sys_nanosleep
        .long  sys_mremap
        .long  sys_setresuid16
        .long  sys_getresuid16           /* 165 */
        .long  sys_ni_syscall            /* for vm86 */
        .long  sys_query_module
        .long  sys_poll
        .long  sys_nfsservctl
        .long  sys_setresgid16           /* 170 */
        .long  sys_getresgid16
        .long  sys_prctl
        .long  sys_rt_sigreturn_glue
        .long  sys_rt_sigaction
        .long  sys_rt_sigprocmask        /* 175 */
        .long  sys_rt_sigpending
        .long  sys_rt_sigtimedwait
        .long  sys_rt_sigqueueinfo
        .long  sys_rt_sigsuspend_glue
        .long  sys_pread                 /* 180 */
        .long  sys_pwrite
        .long  sys_chown16
        .long  sys_getcwd
        .long  sys_capget
        .long  sys_capset                /* 185 */
        .long  sys_sigaltstack_glue
        .long  sys_sendfile
        .long  sys_ni_syscall            /* streams1 */
        .long  sys_ni_syscall            /* streams2 */
        .long  sys_vfork_glue            /* 190 */
        .long  sys_getrlimit
	.long  sys_mmap2
        .long  sys_truncate64
        .long  sys_ftruncate64
        .long  sys_stat64                /* 195 */
        .long  sys_lstat64
        .long  sys_fstat64
	.long  sys_lchown
	.long  sys_getuid
	.long  sys_getgid		 /* 200 */
	.long  sys_geteuid
	.long  sys_getegid
	.long  sys_setreuid
	.long  sys_setregid
	.long  sys_getgroups             /* 205 */
	.long  sys_setgroups
	.long  sys_fchown
	.long  sys_setresuid
	.long  sys_getresuid
	.long  sys_setresgid             /* 210 */
	.long  sys_getresgid
	.long  sys_chown
	.long  sys_setuid
	.long  sys_setgid
	.long  sys_setfsuid              /* 215 */
	.long  sys_setfsgid
        .long  sys_pivot_root
        .long  sys_mincore
        .long  sys_madvise
	.long  sys_getdents64		 /* 220 */
        .long  sys_fcntl64 
	.long  sys_readahead
	.long  sys_ni_syscall
	.long  sys_ni_syscall		 /* 224 - reserved for setxattr  */
	.long  sys_ni_syscall            /* 225 - reserved for lsetxattr */
	.long  sys_ni_syscall		 /* 226 - reserved for fsetxattr */
	.long  sys_ni_syscall		 /* 227 - reserved for getxattr  */
	.long  sys_ni_syscall		 /* 228 - reserved for lgetxattr */
	.long  sys_ni_syscall		 /* 229 - reserved for fgetxattr */
	.long  sys_ni_syscall		 /* 230 - reserved for listxattr */
	.long  sys_ni_syscall		 /* 231 - reserved for llistxattr */
	.long  sys_ni_syscall		 /* 232 - reserved for flistxattr */
	.long  sys_ni_syscall		 /* 233 - reserved for removexattr */
	.long  sys_ni_syscall		 /* 234 - reserved for lremovexattr */
	.long  sys_ni_syscall		 /* 235 - reserved for fremovexattr */
	.long  sys_gettid
	.long  sys_tkill
	.rept  255-237
	.long  sys_ni_syscall
	.endr

/*
 * Program check handler routine
 */

        .globl  pgm_check_handler
pgm_check_handler:
/*
 * First we need to check for a special case:
 * Single stepping an instruction that disables the PER event mask will
 * cause a PER event AFTER the mask has been set. Example: SVC or LPSW.
 * For a single stepped SVC the program check handler gets control after
 * the SVC new PSW has been loaded. But we want to execute the SVC first and
 * then handle the PER event. Therefore we update the SVC old PSW to point
 * to the pgm_check_handler and branch to the SVC handler after we checked
 * if we have to load the kernel stack register.
 * For every other possible cause for PER event without the PER mask set
 * we just ignore the PER event (FIXME: is there anything we have to do
 * for LPSW?).
 */
	SAVE_ALL_BASE
        tm      __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception
        bnz     BASED(pgm_per)           # got per exception -> special case
	SAVE_ALL __LC_PGM_OLD_PSW,1
        l       %r3,__LC_PGM_ILC         # load program interruption code
	la	%r8,0x7f
        l       %r7,BASED(.Ljump_table)
	nr	%r8,%r3
        sll     %r8,2
	GET_CURRENT
        l       %r7,0(%r8,%r7)		 # load address of handler routine
        la      %r2,SP_PTREGS(%r15)	 # address of register-save area
	la      %r14,BASED(sysc_return)
	br      %r7			 # branch to interrupt-handler

#
# handle per exception
#
pgm_per:
        tm      __LC_PGM_OLD_PSW,0x40    # test if per event recording is on
        bnz     BASED(pgm_per_std)       # ok, normal per event from user space
# ok its one of the special cases, now we need to find out which one
        clc     __LC_PGM_OLD_PSW(8),__LC_SVC_NEW_PSW
        be      BASED(pgm_svcper)
# no interesting special case, ignore PER event
        lm      %r13,%r15,__LC_SAVE_AREA
	lpsw    0x28

#
# Normal per exception
#
pgm_per_std:
	SAVE_ALL __LC_PGM_OLD_PSW,1
	GET_CURRENT
	la	%r4,0x7f
	l	%r3,__LC_PGM_ILC	 # load program interruption code
        nr      %r4,%r3                  # clear per-event-bit and ilc
        be      BASED(pgm_per_only)      # only per or per+check ?
        l       %r1,BASED(.Ljump_table)
        sll     %r4,2
        l       %r1,0(%r4,%r1)		 # load address of handler routine
        la      %r2,SP_PTREGS(%r15)	 # address of register-save area
	basr    %r14,%r1		 # branch to interrupt-handler
pgm_per_only:
	la      %r2,SP_PTREGS(15)	 # address of register-save area
        l       %r1,BASED(.Lhandle_per)  # load adr. of per handler
        la      %r14,BASED(sysc_return)  # load adr. of system return
        br      %r1			 # branch to handle_per_exception

#
# it was a single stepped SVC that is causing all the trouble
#
pgm_svcper:
	SAVE_ALL __LC_SVC_OLD_PSW,1
        GET_CURRENT               # load pointer to task_struct to R9
	lh	%r8,0x8a	  # get svc number from lowcore
        sll     %r8,2
        stosm   24(%r15),0x03     # reenable interrupts
        l       %r8,sys_call_table-entry_base(%r8,%r13) # get system call addr.
        tm      __TASK_ptrace+3(%r9),0x02 # PT_TRACESYS
        bnz     BASED(pgm_tracesys)
        basr    %r14,%r8          # call sys_xxxx
        st      %r2,SP_R2(%r15)   # store return value (change R2 on stack)
                                  # ATTENTION: check sys_execve_glue before
                                  # changing anything here !!

pgm_svcret:
        icm     %r0,15,__TASK_sigpending(%r9)
        bz      BASED(pgm_svcper_nosig)
	la	%r2,SP_PTREGS(%r15) # load pt_regs
	sr	%r3,%r3		  # clear *oldset
	l	%r1,BASED(.Ldo_signal)
	basr	%r4,%r1		  # call do_signal
	
pgm_svcper_nosig:
        mvi     SP_TRAP+3(%r15),0x28     # set trap indication to pgm check
	la      %r2,SP_PTREGS(15)        # address of register-save area
        l       %r1,BASED(.Lhandle_per)  # load adr. of per handler
        la      %r14,BASED(sysc_return)  # load adr. of system return
        br      %r1                      # branch to handle_per_exception
#
# call trace before and after sys_call
#
pgm_tracesys:
	la	%r11,BASED(pgm_svcret)
	b	BASED(trace_svc)

/*
 * IO interrupt handler routine
 */

        .globl io_int_handler
io_int_handler:
	SAVE_ALL_BASE
        SAVE_ALL __LC_IO_OLD_PSW,0
	mc 0,0
        GET_CURRENT               # load pointer to task_struct to R9
	stck	__LC_INT_CLOCK
	clc	__LC_INT_CLOCK(8),__LC_JIFFY_TIMER
	bhe	BASED(io_handle_tick)
io_call_handler:
        l       %r1,BASED(.Ldo_IRQ)        # load address of do_IRQ
        la      %r2,SP_PTREGS(%r15) # address of register-save area
        sr      %r3,%r3
        icm     %r3,3,__LC_SUBCHANNEL_NR   # load subchannel nr & extend to int
        l       %r4,__LC_IO_INT_PARM       # load interuption parm
	l       %r5,__LC_IO_INT_WORD       # load interuption word
        basr    %r14,%r1          # branch to standard irq handler

io_return:
#
# check, if bottom-half has to be done
#
        l       %r1,__TASK_processor(%r9)
        sll     %r1,L1_CACHE_SHIFT
        al      %r1,BASED(.Lirq_stat) # get address of irq_stat
        icm     %r0,15,0(%r1)         # test irq_stat[#cpu].__softirq_pending
        bnz     BASED(io_handle_bottom_half)
io_return_bh:

        tm      SP_PSW+1(%r15),0x01 # returning to user ?
        bno     BASED(io_leave)   # no-> skip resched & signal
        stosm   24(%r15),0x03     # reenable interrupts
#
# check, if reschedule is needed
#
        icm     %r0,15,__TASK_need_resched(%r9)
        bnz     BASED(io_reschedule)
        icm     %r0,15,__TASK_sigpending(%r9)
        bnz     BASED(io_signal_return)
io_leave:
        stnsm   24(%r15),0xfc            # disable I/O and ext. interrupts
        RESTORE_ALL 0

#
# account tick
#
io_handle_tick:
	l	%r1,BASED(.Laccount_ticks)
	la	%r2,SP_PTREGS(%r15)    # address of register-save area
	la	%r14,BASED(io_call_handler)
	br	%r1

#
# call do_softirq
#
io_handle_bottom_half:
        l       %r1,BASED(.Ldo_softirq)
        la      %r14,BASED(io_return_bh)
        br      %r1               # call do_softirq

#
# call schedule with io_return as return-address
#
io_reschedule:        
        l       %r1,BASED(.Lschedule)
	la      %r14,BASED(io_return)
        br      %r1               # call scheduler, return to io_return

#
# call do_signal before return
#
io_signal_return:     
        la      %r2,SP_PTREGS(%r15) # load pt_regs
        sr      %r3,%r3           # clear *oldset
        l       %r1,BASED(.Ldo_signal)
	la      %r14,BASED(io_leave)
        br      %r1               # return point is io_leave

/*
 * External interrupt handler routine
 */

        .globl  ext_int_handler
ext_int_handler:
	SAVE_ALL_BASE
        SAVE_ALL __LC_EXT_OLD_PSW,0
	mc 0, 0
	GET_CURRENT                    # load pointer to task_struct to R9
	lh	%r6,__LC_EXT_INT_CODE  # get interruption code
	stck	__LC_INT_CLOCK
	clc	__LC_INT_CLOCK(8),__LC_JIFFY_TIMER
	bhe	BASED(ext_handle_tick)
ext_call_handler:
	lr	%r1,%r6		       # calculate index = code & 0xff
	n	%r1,BASED(.Lc0xff)
	sll	%r1,2
	l	%r7,BASED(.Lext_hash)
	l	%r7,0(%r1,%r7)	       # get first list entry for hash value
	ltr	%r7,%r7		       # == NULL ?
	bz	BASED(io_return)       # yes, nothing to do, exit
ext_int_loop:
	ch	%r6,8(%r7)	       # compare external interrupt code
	bne	BASED(ext_int_next)
	icm	%r1,15,4(%r7)	       # get handler address
	bz	BASED(ext_int_next)
	la	%r2,SP_PTREGS(%r15)    # address of register-save area
	lr	%r3,%r6		       # interruption code
	basr	%r14,%r1	       # call handler
ext_int_next:
	icm	%r7,15,0(%r7)	       # next list entry
	bnz	BASED(ext_int_loop)
	b	BASED(io_return)

#
# account tick
#
ext_handle_tick:
	l	%r1,BASED(.Laccount_ticks)
	la	%r2,SP_PTREGS(%r15)    # address of register-save area
	la	%r14,BASED(ext_call_handler)
	br	%r1

/*
 * Machine check handler routines
 */

        .globl mcck_int_handler
mcck_int_handler:
	SAVE_ALL_BASE
        SAVE_ALL __LC_MCK_OLD_PSW,0
	mc 0, 0
	l       %r1,BASED(.Ls390_mcck)
	basr    %r14,%r1	  # call machine check handler
mcck_return:
        RESTORE_ALL 0

#ifdef CONFIG_SMP
/*
 * Restart interruption handler, kick starter for additional CPUs
 */
        .globl restart_int_handler
restart_int_handler:
        l       %r15,__LC_SAVE_AREA+60 # load ksp
        lctl    %c0,%c15,__LC_CREGS_SAVE_AREA # get new ctl regs
        lam     %a0,%a15,__LC_AREGS_SAVE_AREA
        stosm   0(%r15),0x04           # now we can turn dat on
        lm      %r6,%r15,24(%r15)      # load registers from clone
        basr    %r14,0
        l       %r14,restart_addr-.(%r14)
        br      %r14                   # branch to start_secondary
restart_addr:
        .long   start_secondary
#else
/*
 * If we do not run with SMP enabled, let the new CPU crash ...
 */
        .globl restart_int_handler
restart_int_handler:
        basr    %r1,0
restart_base:
        lpsw    restart_crash-restart_base(%r1)
        .align 8
restart_crash:
        .long  0x000a0000,0x00000000
restart_go:
#endif

/*
 * Integer constants
 */
               .align 4
.Lc0xfffffff8: .long  -8           # to align stack pointer to 8
.Lc0xffffe000: .long  -8192        # to round stack pointer to &task_struct
.Lc8191:       .long  8191
.Lc_spsize:    .long  SP_SIZE
.Lc_overhead:  .long  STACK_FRAME_OVERHEAD
.Lc_ac:        .long  0,0,1
.Lc_ENOSYS:    .long  -ENOSYS
.Lc4:          .long  4
.Lc20:         .long  20
.Lc0x1202:     .long  0x1202
.Lc0x1004:     .long  0x1004
.Lc0x2401:     .long  0x2401
.Lc0x4000:     .long  0x4000
.Lc0xff:       .long  0xff
.Lc128:        .long  128

/*
 * Symbol constants
 */
.Ls390_mcck:   .long  s390_do_machine_check
.Ldo_IRQ:      .long  do_IRQ
.Ldo_signal:   .long  do_signal
.Ldo_softirq:  .long  do_softirq
.Lentry_base:  .long  entry_base
.Lext_hash:    .long  ext_int_hash
.Lhandle_per:  .long  handle_per_exception
.Lirq_stat:    .long  irq_stat
.Ljump_table:  .long  pgm_check_table
.Lschedule:    .long  schedule
.Lclone:       .long  sys_clone
.Lexecve:      .long  sys_execve
.Lfork:        .long  sys_fork
.Lrt_sigreturn:.long  sys_rt_sigreturn
.Lrt_sigsuspend:
               .long  sys_rt_sigsuspend
.Lsigreturn:   .long  sys_sigreturn
.Lsigsuspend:  .long  sys_sigsuspend
.Lsigaltstack: .long  sys_sigaltstack
.Ltrace:       .long  syscall_trace
.Lvfork:       .long  sys_vfork

.Lschedtail:   .long  schedule_tail
.Laccount_ticks:.long account_ticks
