/* * 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 #include #include #include #include #include #include #include #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