/* * ia64/kernel/entry.S * * Kernel entry points. * * Copyright (C) 1998-2002 Hewlett-Packard Co * David Mosberger-Tang * Copyright (C) 1999 VA Linux Systems * Copyright (C) 1999 Walt Drummond * Copyright (C) 1999 Asit Mallick * Copyright (C) 1999 Don Dugger */ /* * ia64_switch_to now places correct virtual mapping in in TR2 for * kernel stack. This allows us to handle interrupts without changing * to physical mode. * * Jonathan Nicklin * Patrick O'Rourke * 11/07/2000 / /* * Global (preserved) predicate usage on syscall entry/exit path: * * pKern: See entry.h. * pUser: See entry.h. * pSys: See entry.h. * pNonSys: !pSys */ #include #include #include #include #include #include #include #include #include #include "minstate.h" /* * execve() is special because in case of success, we need to * setup a null register window frame. */ ENTRY(ia64_execve) .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(3) alloc loc1=ar.pfs,3,2,4,0 mov loc0=rp .body mov out0=in0 // filename ;; // stop bit between alloc and call mov out1=in1 // argv mov out2=in2 // envp add out3=16,sp // regs br.call.sptk.many rp=sys_execve .ret0: cmp4.ge p6,p7=r8,r0 mov ar.pfs=loc1 // restore ar.pfs sxt4 r8=r8 // return 64-bit result ;; stf.spill [sp]=f0 (p6) cmp.ne pKern,pUser=r0,r0 // a successful execve() lands us in user-mode... mov rp=loc0 (p6) mov ar.pfs=r0 // clear ar.pfs on success (p7) br.ret.sptk.many rp /* * In theory, we'd have to zap this state only to prevent leaking of * security sensitive state (e.g., if current->mm->dumpable is zero). However, * this executes in less than 20 cycles even on Itanium, so it's not worth * optimizing for...). */ mov r4=0; mov f2=f0; mov b1=r0 mov r5=0; mov f3=f0; mov b2=r0 mov r6=0; mov f4=f0; mov b3=r0 mov r7=0; mov f5=f0; mov b4=r0 mov ar.unat=0; mov f10=f0; mov b5=r0 ldf.fill f11=[sp]; ldf.fill f12=[sp]; mov f13=f0 ldf.fill f14=[sp]; ldf.fill f15=[sp]; mov f16=f0 ldf.fill f17=[sp]; ldf.fill f18=[sp]; mov f19=f0 ldf.fill f20=[sp]; ldf.fill f21=[sp]; mov f22=f0 ldf.fill f23=[sp]; ldf.fill f24=[sp]; mov f25=f0 ldf.fill f26=[sp]; ldf.fill f27=[sp]; mov f28=f0 ldf.fill f29=[sp]; ldf.fill f30=[sp]; mov f31=f0 mov ar.lc=0 br.ret.sptk.many rp END(ia64_execve) GLOBAL_ENTRY(sys_clone2) .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) alloc r16=ar.pfs,3,2,4,0 DO_SAVE_SWITCH_STACK mov loc0=rp mov loc1=r16 // save ar.pfs across do_fork .body mov out1=in1 mov out3=in2 adds out2=IA64_SWITCH_STACK_SIZE+16,sp // out2 = ®s mov out0=in0 // out0 = clone_flags br.call.sptk.many rp=do_fork .ret1: .restore sp adds sp=IA64_SWITCH_STACK_SIZE,sp // pop the switch stack mov ar.pfs=loc1 mov rp=loc0 br.ret.sptk.many rp END(sys_clone2) GLOBAL_ENTRY(sys_clone) .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) alloc r16=ar.pfs,2,2,4,0 DO_SAVE_SWITCH_STACK mov loc0=rp mov loc1=r16 // save ar.pfs across do_fork .body mov out1=in1 mov out3=16 // stacksize (compensates for 16-byte scratch area) adds out2=IA64_SWITCH_STACK_SIZE+16,sp // out2 = ®s mov out0=in0 // out0 = clone_flags br.call.sptk.many rp=do_fork .ret2: .restore sp adds sp=IA64_SWITCH_STACK_SIZE,sp // pop the switch stack mov ar.pfs=loc1 mov rp=loc0 br.ret.sptk.many rp END(sys_clone) /* * prev_task <- ia64_switch_to(struct task_struct *next) */ GLOBAL_ENTRY(ia64_switch_to) .prologue alloc r16=ar.pfs,1,0,0,0 DO_SAVE_SWITCH_STACK .body adds r22=IA64_TASK_THREAD_KSP_OFFSET,r13 mov r27=IA64_KR(CURRENT_STACK) dep r20=0,in0,61,3 // physical address of "current" ;; st8 [r22]=sp // save kernel stack pointer of old task shr.u r26=r20,IA64_GRANULE_SHIFT shr.u r17=r20,KERNEL_TR_PAGE_SHIFT ;; cmp.ne p6,p7=KERNEL_TR_PAGE_NUM,r17 adds r21=IA64_TASK_THREAD_KSP_OFFSET,in0 ;; /* * If we've already mapped this task's page, we can skip doing it again. */ (p6) cmp.eq p7,p6=r26,r27 (p6) br.cond.dpnt .map ;; .done: (p6) ssm psr.ic // if we we had to map, renable the psr.ic bit FIRST!!! ;; (p6) srlz.d ld8 sp=[r21] // load kernel stack pointer of new task mov IA64_KR(CURRENT)=r20 // update "current" application register mov r8=r13 // return pointer to previously running task mov r13=in0 // set "current" pointer ;; (p6) ssm psr.i // renable psr.i AFTER the ic bit is serialized DO_LOAD_SWITCH_STACK #ifdef CONFIG_SMP sync.i // ensure "fc"s done by this CPU are visible on other CPUs #endif br.ret.sptk.many rp // boogie on out in new context .map: rsm psr.i | psr.ic movl r25=PAGE_KERNEL ;; srlz.d or r23=r25,r20 // construct PA | page properties mov r25=IA64_GRANULE_SHIFT<<2 ;; mov cr.itir=r25 mov cr.ifa=in0 // VA of next task... ;; mov r25=IA64_TR_CURRENT_STACK mov IA64_KR(CURRENT_STACK)=r26 // remember last page we mapped... ;; itr.d dtr[r25]=r23 // wire in new mapping... br.cond.sptk .done END(ia64_switch_to) /* * Note that interrupts are enabled during save_switch_stack and * load_switch_stack. This means that we may get an interrupt with * "sp" pointing to the new kernel stack while ar.bspstore is still * pointing to the old kernel backing store area. Since ar.rsc, * ar.rnat, ar.bsp, and ar.bspstore are all preserved by interrupts, * this is not a problem. Also, we don't need to specify unwind * information for preserved registers that are not modified in * save_switch_stack as the right unwind information is already * specified at the call-site of save_switch_stack. */ /* * save_switch_stack: * - r16 holds ar.pfs * - b7 holds address to return to * - rp (b0) holds return address to save */ GLOBAL_ENTRY(save_switch_stack) .prologue .altrp b7 flushrs // flush dirty regs to backing store (must be first in insn group) .save @priunat,r17 mov r17=ar.unat // preserve caller's .body adds r3=80,sp ;; lfetch.fault.excl.nt1 [r3],128 mov ar.rsc=0 // put RSE in mode: enforced lazy, little endian, pl 0 adds r2=16+128,sp ;; lfetch.fault.excl.nt1 [r2],128 lfetch.fault.excl.nt1 [r3],128 adds r14=SW(R4)+16,sp ;; lfetch.fault.excl [r2] lfetch.fault.excl [r3] adds r15=SW(R5)+16,sp ;; mov r18=ar.fpsr // preserve fpsr mov r19=ar.rnat add r2=SW(F2)+16,sp // r2 = &sw->f2 .mem.offset 0,0; st8.spill [r14]=r4,16 // spill r4 .mem.offset 8,0; st8.spill [r15]=r5,16 // spill r5 add r3=SW(F3)+16,sp // r3 = &sw->f3 ;; stf.spill [r2]=f2,32 stf.spill [r3]=f3,32 mov r21=b0 .mem.offset 0,0; st8.spill [r14]=r6,16 // spill r6 .mem.offset 8,0; st8.spill [r15]=r7,16 // spill r7 mov r22=b1 ;; // since we're done with the spills, read and save ar.unat: mov r29=ar.unat // M-unit mov r20=ar.bspstore // M-unit mov r23=b2 stf.spill [r2]=f4,32 stf.spill [r3]=f5,32 mov r24=b3 ;; st8 [r14]=r21,16 // save b0 st8 [r15]=r22,16 // save b1 mov r25=b4 stf.spill [r2]=f10,32 stf.spill [r3]=f11,32 mov r26=b5 ;; st8 [r14]=r23,16 // save b2 st8 [r15]=r24,16 // save b3 mov r21=ar.lc // I-unit stf.spill [r2]=f12,32 stf.spill [r3]=f13,32 ;; st8 [r14]=r25,16 // save b4 st8 [r15]=r26,16 // save b5 stf.spill [r2]=f14,32 stf.spill [r3]=f15,32 ;; st8 [r14]=r16 // save ar.pfs st8 [r15]=r21 // save ar.lc stf.spill [r2]=f16,32 stf.spill [r3]=f17,32 ;; stf.spill [r2]=f18,32 stf.spill [r3]=f19,32 ;; stf.spill [r2]=f20,32 stf.spill [r3]=f21,32 ;; stf.spill [r2]=f22,32 stf.spill [r3]=f23,32 ;; stf.spill [r2]=f24,32 stf.spill [r3]=f25,32 add r14=SW(CALLER_UNAT)+16,sp ;; stf.spill [r2]=f26,32 stf.spill [r3]=f27,32 add r15=SW(AR_FPSR)+16,sp ;; stf.spill [r2]=f28,32 stf.spill [r3]=f29,32 st8 [r14]=r17 // save caller_unat st8 [r15]=r18 // save fpsr mov r21=pr ;; stf.spill [r2]=f30,(SW(AR_UNAT)-SW(F30)) stf.spill [r3]=f31,(SW(AR_RNAT)-SW(F31)) ;; st8 [r2]=r29,16 // save ar.unat st8 [r3]=r19,16 // save ar.rnat ;; st8 [r2]=r20 // save ar.bspstore st8 [r3]=r21 // save predicate registers mov ar.rsc=3 // put RSE back into eager mode, pl 0 br.cond.sptk.many b7 END(save_switch_stack) /* * load_switch_stack: * - "invala" MUST be done at call site (normally in DO_LOAD_SWITCH_STACK) * - b7 holds address to return to * - must not touch r8-r11 */ ENTRY(load_switch_stack) .prologue .altrp b7 .body lfetch.fault.nt1 [sp] adds r2=SW(AR_BSPSTORE)+16,sp adds r3=SW(AR_UNAT)+16,sp mov ar.rsc=0 // put RSE into enforced lazy mode adds r14=SW(CALLER_UNAT)+16,sp adds r15=SW(AR_FPSR)+16,sp ;; ld8 r27=[r2],(SW(B0)-SW(AR_BSPSTORE)) // bspstore ld8 r29=[r3],(SW(B1)-SW(AR_UNAT)) // unat ;; ld8 r21=[r2],16 // restore b0 ld8 r22=[r3],16 // restore b1 ;; ld8 r23=[r2],16 // restore b2 ld8 r24=[r3],16 // restore b3 ;; ld8 r25=[r2],16 // restore b4 ld8 r26=[r3],16 // restore b5 ;; ld8 r16=[r2],(SW(PR)-SW(AR_PFS)) // ar.pfs ld8 r17=[r3],(SW(AR_RNAT)-SW(AR_LC)) // ar.lc ;; ld8 r28=[r2] // restore pr ld8 r30=[r3] // restore rnat ;; ld8 r18=[r14],16 // restore caller's unat ld8 r19=[r15],24 // restore fpsr ;; ldf.fill f2=[r14],32 ldf.fill f3=[r15],32 ;; ldf.fill f4=[r14],32 ldf.fill f5=[r15],32 ;; ldf.fill f10=[r14],32 ldf.fill f11=[r15],32 ;; ldf.fill f12=[r14],32 ldf.fill f13=[r15],32 ;; ldf.fill f14=[r14],32 ldf.fill f15=[r15],32 ;; ldf.fill f16=[r14],32 ldf.fill f17=[r15],32 ;; ldf.fill f18=[r14],32 ldf.fill f19=[r15],32 mov b0=r21 ;; ldf.fill f20=[r14],32 ldf.fill f21=[r15],32 mov b1=r22 ;; ldf.fill f22=[r14],32 ldf.fill f23=[r15],32 mov b2=r23 ;; mov ar.bspstore=r27 mov ar.unat=r29 // establish unat holding the NaT bits for r4-r7 mov b3=r24 ;; ldf.fill f24=[r14],32 ldf.fill f25=[r15],32 mov b4=r25 ;; ldf.fill f26=[r14],32 ldf.fill f27=[r15],32 mov b5=r26 ;; ldf.fill f28=[r14],32 ldf.fill f29=[r15],32 mov ar.pfs=r16 ;; ldf.fill f30=[r14],32 ldf.fill f31=[r15],24 mov ar.lc=r17 ;; ld8.fill r4=[r14],16 ld8.fill r5=[r15],16 mov pr=r28,-1 ;; ld8.fill r6=[r14],16 ld8.fill r7=[r15],16 mov ar.unat=r18 // restore caller's unat mov ar.rnat=r30 // must restore after bspstore but before rsc! mov ar.fpsr=r19 // restore fpsr mov ar.rsc=3 // put RSE back into eager mode, pl 0 br.cond.sptk.many b7 END(load_switch_stack) GLOBAL_ENTRY(__ia64_syscall) .regstk 6,0,0,0 mov r15=in5 // put syscall number in place break __BREAK_SYSCALL movl r2=errno cmp.eq p6,p7=-1,r10 ;; (p6) st4 [r2]=r8 (p6) mov r8=-1 br.ret.sptk.many rp END(__ia64_syscall) /* * We invoke syscall_trace through this intermediate function to * ensure that the syscall input arguments are not clobbered. We * also use it to preserve b6, which contains the syscall entry point. */ GLOBAL_ENTRY(invoke_syscall_trace) .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) alloc loc1=ar.pfs,8,3,0,0 mov loc0=rp .body mov loc2=b6 ;; br.call.sptk.many rp=syscall_trace .ret3: mov rp=loc0 mov ar.pfs=loc1 mov b6=loc2 br.ret.sptk.many rp END(invoke_syscall_trace) /* * Invoke a system call, but do some tracing before and after the call. * We MUST preserve the current register frame throughout this routine * because some system calls (such as ia64_execve) directly * manipulate ar.pfs. * * Input: * r15 = syscall number * b6 = syscall entry point */ .global ia64_strace_leave_kernel GLOBAL_ENTRY(ia64_trace_syscall) PT_REGS_UNWIND_INFO(0) br.call.sptk.many rp=invoke_syscall_trace // give parent a chance to catch syscall args .ret6: br.call.sptk.many rp=b6 // do the syscall strace_check_retval: cmp.lt p6,p0=r8,r0 // syscall failed? adds r2=PT(R8)+16,sp // r2 = &pt_regs.r8 adds r3=PT(R10)+16,sp // r3 = &pt_regs.r10 mov r10=0 (p6) br.cond.sptk strace_error // syscall failed -> ;; // avoid RAW on r10 strace_save_retval: .mem.offset 0,0; st8.spill [r2]=r8 // store return value in slot for r8 .mem.offset 8,0; st8.spill [r3]=r10 // clear error indication in slot for r10 ia64_strace_leave_kernel: br.call.sptk.many rp=invoke_syscall_trace // give parent a chance to catch return value .rety: br.cond.sptk ia64_leave_kernel strace_error: ld8 r3=[r2] // load pt_regs.r8 sub r9=0,r8 // negate return value to get errno value ;; cmp.ne p6,p0=r3,r0 // is pt_regs.r8!=0? adds r3=16,r2 // r3=&pt_regs.r10 ;; (p6) mov r10=-1 (p6) mov r8=r9 br.cond.sptk strace_save_retval END(ia64_trace_syscall) GLOBAL_ENTRY(ia64_ret_from_clone) PT_REGS_UNWIND_INFO(0) /* * We need to call schedule_tail() to complete the scheduling process. * Called by ia64_switch_to after do_fork()->copy_thread(). r8 contains the * address of the previously executing task. */ br.call.sptk.many rp=ia64_invoke_schedule_tail .ret8: adds r2=IA64_TASK_PTRACE_OFFSET,r13 ;; ld8 r2=[r2] ;; mov r8=0 tbit.nz p6,p0=r2,PT_TRACESYS_BIT (p6) br.cond.spnt strace_check_retval ;; // added stop bits to prevent r8 dependency END(ia64_ret_from_clone) // fall through GLOBAL_ENTRY(ia64_ret_from_syscall) PT_REGS_UNWIND_INFO(0) cmp.ge p6,p7=r8,r0 // syscall executed successfully? adds r2=PT(R8)+16,sp // r2 = &pt_regs.r8 adds r3=PT(R10)+16,sp // r3 = &pt_regs.r10 ;; .mem.offset 0,0 (p6) st8.spill [r2]=r8 // store return value in slot for r8 and set unat bit .mem.offset 8,0 (p6) st8.spill [r3]=r0 // clear error indication in slot for r10 and set unat bit (p7) br.cond.spnt handle_syscall_error // handle potential syscall failure END(ia64_ret_from_syscall) // fall through GLOBAL_ENTRY(ia64_leave_kernel) PT_REGS_UNWIND_INFO(0) lfetch.fault [sp] movl r14=.restart ;; mov.ret.sptk rp=r14,.restart .restart: // need_resched and signals atomic test (pUser) rsm psr.i adds r17=IA64_TASK_NEED_RESCHED_OFFSET,r13 adds r18=IA64_TASK_SIGPENDING_OFFSET,r13 #ifdef CONFIG_PERFMON adds r19=IA64_TASK_PFM_OVFL_BLOCK_RESET_OFFSET,r13 #endif ;; #ifdef CONFIG_PERFMON (pUser) ld8 r19=[r19] // load current->thread.pfm_ovfl_block_reset #endif (pUser) ld8 r17=[r17] // load current->need_resched (pUser) ld4 r18=[r18] // load current->sigpending ;; #ifdef CONFIG_PERFMON (pUser) cmp.ne.unc p9,p0=r19,r0 // current->thread.pfm_ovfl_block_reset != 0? #endif (pUser) cmp.ne.unc p7,p0=r17,r0 // current->need_resched != 0? (pUser) cmp.ne.unc p8,p0=r18,r0 // current->sigpending != 0? ;; #ifdef CONFIG_PERFMON (p9) br.call.spnt.many b7=pfm_ovfl_block_reset #endif #if __GNUC__ < 3 (p7) br.call.spnt.many b7=invoke_schedule #else (p7) br.call.spnt.many b7=schedule #endif (p8) br.call.spnt.many rp=handle_signal_delivery // check & deliver pending signals (once) ;; .ret9: adds r2=PT(R8)+16,r12 adds r3=PT(R9)+16,r12 ;; // start restoring the state saved on the kernel stack (struct pt_regs): ld8.fill r8=[r2],16 ld8.fill r9=[r3],16 ;; ld8.fill r10=[r2],16 ld8.fill r11=[r3],16 ;; ld8.fill r16=[r2],16 ld8.fill r17=[r3],16 ;; ld8.fill r18=[r2],16 ld8.fill r19=[r3],16 ;; ld8.fill r20=[r2],16 ld8.fill r21=[r3],16 ;; ld8.fill r22=[r2],16 ld8.fill r23=[r3],16 ;; ld8.fill r24=[r2],16 ld8.fill r25=[r3],16 ;; ld8.fill r26=[r2],16 ld8.fill r27=[r3],16 ;; ld8.fill r28=[r2],16 ld8.fill r29=[r3],16 ;; ld8.fill r30=[r2],16 ld8.fill r31=[r3],16 ;; rsm psr.i | psr.ic // initiate turning off of interrupt and interruption collection invala // invalidate ALAT ;; ld8 r1=[r2],16 // ar.ccv ld8 r13=[r3],16 // ar.fpsr ;; ld8 r14=[r2],16 // b0 ld8 r15=[r3],16+8 // b7 ;; ldf.fill f6=[r2],32 ldf.fill f7=[r3],32 ;; ldf.fill f8=[r2],32 ldf.fill f9=[r3],32 ;; mov ar.ccv=r1 mov ar.fpsr=r13 mov b0=r14 ;; srlz.i // ensure interruption collection is off mov b7=r15 ;; bsw.0 // switch back to bank 0 ;; adds r16=16,r12 adds r17=24,r12 ;; ld8 rCRIPSR=[r16],16 // load cr.ipsr ld8 rCRIIP=[r17],16 // load cr.iip ;; ld8 rCRIFS=[r16],16 // load cr.ifs ld8 rARUNAT=[r17],16 // load ar.unat cmp.eq p9,p0=r0,r0 // set p9 to indicate that we should restore cr.ifs ;; ld8 rARPFS=[r16],16 // load ar.pfs ld8 rARRSC=[r17],16 // load ar.rsc ;; ld8 rARRNAT=[r16],16 // load ar.rnat (may be garbage) ld8 rARBSPSTORE=[r17],16 // load ar.bspstore (may be garbage) ;; ld8 rARPR=[r16],16 // load predicates ld8 rB6=[r17],16 // load b6 ;; ld8 r19=[r16],16 // load ar.rsc value for "loadrs" ld8.fill r1=[r17],16 // load r1 ;; ld8.fill r2=[r16],16 ld8.fill r3=[r17],16 ;; ld8.fill r12=[r16],16 ld8.fill r13=[r17],16 ;; ld8.fill r14=[r16] ld8.fill r15=[r17] shr.u r18=r19,16 // get byte size of existing "dirty" partition ;; mov r16=ar.bsp // get existing backing store pointer movl r17=PERCPU_ADDR+IA64_CPU_PHYS_STACKED_SIZE_P8_OFFSET ;; ld4 r17=[r17] // r17 = cpu_data->phys_stacked_size_p8 (pKern) br.cond.dpnt skip_rbs_switch /* * Restore user backing store. * * NOTE: alloc, loadrs, and cover can't be predicated. */ (pNonSys) br.cond.dpnt dont_preserve_current_frame cover // add current frame into dirty partition ;; mov r19=ar.bsp // get new backing store pointer sub r16=r16,r18 // krbs = old bsp - size of dirty partition cmp.ne p9,p0=r0,r0 // clear p9 to skip restore of cr.ifs ;; sub r19=r19,r16 // calculate total byte size of dirty partition add r18=64,r18 // don't force in0-in7 into memory... ;; shl r19=r19,16 // shift size of dirty partition into loadrs position ;; dont_preserve_current_frame: /* * To prevent leaking bits between the kernel and user-space, * we must clear the stacked registers in the "invalid" partition here. * Not pretty, but at least it's fast (3.34 registers/cycle on Itanium, * 5 registers/cycle on McKinley). */ # define pRecurse p6 # define pReturn p7 #ifdef CONFIG_ITANIUM # define Nregs 10 #else # define Nregs 14 #endif alloc loc0=ar.pfs,2,Nregs-2,2,0 shr.u loc1=r18,9 // RNaTslots <= dirtySize / (64*8) + 1 sub r17=r17,r18 // r17 = (physStackedSize + 8) - dirtySize ;; #if 1 .align 32 // see comment below about gas bug... #endif mov ar.rsc=r19 // load ar.rsc to be used for "loadrs" shladd in0=loc1,3,r17 mov in1=0 #if 0 // gas-2.11.90 is unable to generate a stop bit after .align, which is bad, // because alloc must be at the beginning of an insn-group. .align 32 #else nop 0 nop 0 nop 0 #endif ;; rse_clear_invalid: #ifdef CONFIG_ITANIUM // cycle 0 { .mii alloc loc0=ar.pfs,2,Nregs-2,2,0 cmp.lt pRecurse,p0=Nregs*8,in0 // if more than Nregs regs left to clear, (re)curse add out0=-Nregs*8,in0 }{ .mfb add out1=1,in1 // increment recursion count nop.f 0 nop.b 0 // can't do br.call here because of alloc (WAW on CFM) ;; }{ .mfi // cycle 1 mov loc1=0 nop.f 0 mov loc2=0 }{ .mib mov loc3=0 mov loc4=0 (pRecurse) br.call.sptk.many b6=rse_clear_invalid }{ .mfi // cycle 2 mov loc5=0 nop.f 0 cmp.ne pReturn,p0=r0,in1 // if recursion count != 0, we need to do a br.ret }{ .mib mov loc6=0 mov loc7=0 (pReturn) br.ret.sptk.many b6 } #else /* !CONFIG_ITANIUM */ alloc loc0=ar.pfs,2,Nregs-2,2,0 cmp.lt pRecurse,p0=Nregs*8,in0 // if more than Nregs regs left to clear, (re)curse add out0=-Nregs*8,in0 add out1=1,in1 // increment recursion count mov loc1=0 mov loc2=0 ;; mov loc3=0 mov loc4=0 mov loc9=0 mov loc5=0 mov loc6=0 (pRecurse) br.call.sptk.many b6=rse_clear_invalid ;; mov loc7=0 mov loc8=0 cmp.ne pReturn,p0=r0,in1 // if recursion count != 0, we need to do a br.ret mov loc10=0 mov loc11=0 (pReturn) br.ret.sptk.many b6 #endif /* !CONFIG_ITANIUM */ # undef pRecurse # undef pReturn ;; alloc r17=ar.pfs,0,0,0,0 // drop current register frame ;; loadrs ;; skip_rbs_switch: mov b6=rB6 mov ar.pfs=rARPFS (pUser) mov ar.bspstore=rARBSPSTORE (p9) mov cr.ifs=rCRIFS mov cr.ipsr=rCRIPSR mov cr.iip=rCRIIP ;; (pUser) mov ar.rnat=rARRNAT // must happen with RSE in lazy mode mov ar.rsc=rARRSC mov ar.unat=rARUNAT mov pr=rARPR,-1 rfi END(ia64_leave_kernel) ENTRY(handle_syscall_error) /* * Some system calls (e.g., ptrace, mmap) can return arbitrary * values which could lead us to mistake a negative return * value as a failed syscall. Those syscall must deposit * a non-zero value in pt_regs.r8 to indicate an error. * If pt_regs.r8 is zero, we assume that the call completed * successfully. */ PT_REGS_UNWIND_INFO(0) ld8 r3=[r2] // load pt_regs.r8 sub r9=0,r8 // negate return value to get errno ;; mov r10=-1 // return -1 in pt_regs.r10 to indicate error cmp.eq p6,p7=r3,r0 // is pt_regs.r8==0? adds r3=16,r2 // r3=&pt_regs.r10 ;; (p6) mov r9=r8 (p6) mov r10=0 ;; .mem.offset 0,0; st8.spill [r2]=r9 // store errno in pt_regs.r8 and set unat bit .mem.offset 8,0; st8.spill [r3]=r10 // store error indication in pt_regs.r10 and set unat bit br.cond.sptk ia64_leave_kernel END(handle_syscall_error) /* * Invoke schedule_tail(task) while preserving in0-in7, which may be needed * in case a system call gets restarted. */ GLOBAL_ENTRY(ia64_invoke_schedule_tail) .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) alloc loc1=ar.pfs,8,2,1,0 mov loc0=rp mov out0=r8 // Address of previous task ;; br.call.sptk.many rp=schedule_tail .ret11: mov ar.pfs=loc1 mov rp=loc0 br.ret.sptk.many rp END(ia64_invoke_schedule_tail) #if __GNUC__ < 3 /* * Invoke schedule() while preserving in0-in7, which may be needed * in case a system call gets restarted. Note that declaring schedule() * with asmlinkage() is NOT enough because that will only preserve as many * registers as there are formal arguments. * * XXX fix me: with gcc 3.0, we won't need this anymore because syscall_linkage * renders all eight input registers (in0-in7) as "untouchable". */ ENTRY(invoke_schedule) .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) alloc loc1=ar.pfs,8,2,0,0 mov loc0=rp ;; .body br.call.sptk.many rp=schedule .ret14: mov ar.pfs=loc1 mov rp=loc0 br.ret.sptk.many rp END(invoke_schedule) #endif /* __GNUC__ < 3 */ /* * Setup stack and call ia64_do_signal. Note that pSys and pNonSys need to * be set up by the caller. We declare 8 input registers so the system call * args get preserved, in case we need to restart a system call. */ ENTRY(handle_signal_delivery) .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) alloc loc1=ar.pfs,8,2,3,0 // preserve all eight input regs in case of syscall restart! mov r9=ar.unat mov loc0=rp // save return address mov out0=0 // there is no "oldset" adds out1=0,sp // out1=&sigscratch (pSys) mov out2=1 // out2==1 => we're in a syscall ;; (pNonSys) mov out2=0 // out2==0 => not a syscall .fframe 16 .spillpsp ar.unat, 16 // (note that offset is relative to psp+0x10!) st8 [sp]=r9,-16 // allocate space for ar.unat and save it .body br.call.sptk.many rp=ia64_do_signal .ret15: .restore sp adds sp=16,sp // pop scratch stack space ;; ld8 r9=[sp] // load new unat from sw->caller_unat mov rp=loc0 ;; mov ar.unat=r9 mov ar.pfs=loc1 br.ret.sptk.many rp END(handle_signal_delivery) GLOBAL_ENTRY(sys_rt_sigsuspend) .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8) alloc loc1=ar.pfs,8,2,3,0 // preserve all eight input regs in case of syscall restart! mov r9=ar.unat mov loc0=rp // save return address mov out0=in0 // mask mov out1=in1 // sigsetsize adds out2=0,sp // out2=&sigscratch ;; .fframe 16 .spillpsp ar.unat, 16 // (note that offset is relative to psp+0x10!) st8 [sp]=r9,-16 // allocate space for ar.unat and save it .body br.call.sptk.many rp=ia64_rt_sigsuspend .ret17: .restore sp adds sp=16,sp // pop scratch stack space ;; ld8 r9=[sp] // load new unat from sw->caller_unat mov rp=loc0 ;; mov ar.unat=r9 mov ar.pfs=loc1 br.ret.sptk.many rp END(sys_rt_sigsuspend) ENTRY(sys_rt_sigreturn) PT_REGS_UNWIND_INFO(0) alloc r2=ar.pfs,0,0,1,0 .prologue PT_REGS_SAVES(16) adds sp=-16,sp .body cmp.eq pNonSys,pSys=r0,r0 // sigreturn isn't a normal syscall... ;; adds out0=16,sp // out0 = &sigscratch br.call.sptk.many rp=ia64_rt_sigreturn .ret19: .restore sp 0 adds sp=16,sp ;; ld8 r9=[sp] // load new ar.unat mov.sptk b7=r8,ia64_leave_kernel ;; mov ar.unat=r9 br.many b7 END(sys_rt_sigreturn) GLOBAL_ENTRY(ia64_prepare_handle_unaligned) // // r16 = fake ar.pfs, we simply need to make sure // privilege is still 0 // mov r16=r0 .prologue DO_SAVE_SWITCH_STACK br.call.sptk.many rp=ia64_handle_unaligned // stack frame setup in ivt .ret21: .body DO_LOAD_SWITCH_STACK br.cond.sptk.many rp // goes to ia64_leave_kernel END(ia64_prepare_handle_unaligned) // // unw_init_running(void (*callback)(info, arg), void *arg) // # define EXTRA_FRAME_SIZE ((UNW_FRAME_INFO_SIZE+15)&~15) GLOBAL_ENTRY(unw_init_running) .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) alloc loc1=ar.pfs,2,3,3,0 ;; ld8 loc2=[in0],8 mov loc0=rp mov r16=loc1 DO_SAVE_SWITCH_STACK .body .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) .fframe IA64_SWITCH_STACK_SIZE+EXTRA_FRAME_SIZE SWITCH_STACK_SAVES(EXTRA_FRAME_SIZE) adds sp=-EXTRA_FRAME_SIZE,sp .body ;; adds out0=16,sp // &info mov out1=r13 // current adds out2=16+EXTRA_FRAME_SIZE,sp // &switch_stack br.call.sptk.many rp=unw_init_frame_info 1: adds out0=16,sp // &info mov b6=loc2 mov loc2=gp // save gp across indirect function call ;; ld8 gp=[in0] mov out1=in1 // arg br.call.sptk.many rp=b6 // invoke the callback function 1: mov gp=loc2 // restore gp // For now, we don't allow changing registers from within // unw_init_running; if we ever want to allow that, we'd // have to do a load_switch_stack here: .restore sp adds sp=IA64_SWITCH_STACK_SIZE+EXTRA_FRAME_SIZE,sp mov ar.pfs=loc1 mov rp=loc0 br.ret.sptk.many rp END(unw_init_running) .rodata .align 8 .globl sys_call_table sys_call_table: data8 sys_ni_syscall // This must be sys_ni_syscall! See ivt.S. data8 sys_exit // 1025 data8 sys_read data8 sys_write data8 sys_open data8 sys_close data8 sys_creat // 1030 data8 sys_link data8 sys_unlink data8 ia64_execve data8 sys_chdir data8 sys_fchdir // 1035 data8 sys_utimes data8 sys_mknod data8 sys_chmod data8 sys_chown data8 sys_lseek // 1040 data8 sys_getpid data8 sys_getppid data8 sys_mount data8 sys_umount data8 sys_setuid // 1045 data8 sys_getuid data8 sys_geteuid data8 sys_ptrace data8 sys_access data8 sys_sync // 1050 data8 sys_fsync data8 sys_fdatasync data8 sys_kill data8 sys_rename data8 sys_mkdir // 1055 data8 sys_rmdir data8 sys_dup data8 sys_pipe data8 sys_times data8 ia64_brk // 1060 data8 sys_setgid data8 sys_getgid data8 sys_getegid data8 sys_acct data8 sys_ioctl // 1065 data8 sys_fcntl data8 sys_umask data8 sys_chroot data8 sys_ustat data8 sys_dup2 // 1070 data8 sys_setreuid data8 sys_setregid data8 sys_getresuid data8 sys_setresuid data8 sys_getresgid // 1075 data8 sys_setresgid data8 sys_getgroups data8 sys_setgroups data8 sys_getpgid data8 sys_setpgid // 1080 data8 sys_setsid data8 sys_getsid data8 sys_sethostname data8 sys_setrlimit data8 sys_getrlimit // 1085 data8 sys_getrusage data8 sys_gettimeofday data8 sys_settimeofday data8 sys_select data8 sys_poll // 1090 data8 sys_symlink data8 sys_readlink data8 sys_uselib data8 sys_swapon data8 sys_swapoff // 1095 data8 sys_reboot data8 sys_truncate data8 sys_ftruncate data8 sys_fchmod data8 sys_fchown // 1100 data8 ia64_getpriority data8 sys_setpriority data8 sys_statfs data8 sys_fstatfs data8 sys_gettid // 1105 data8 sys_semget data8 sys_semop data8 sys_semctl data8 sys_msgget data8 sys_msgsnd // 1110 data8 sys_msgrcv data8 sys_msgctl data8 sys_shmget data8 ia64_shmat data8 sys_shmdt // 1115 data8 sys_shmctl data8 sys_syslog data8 sys_setitimer data8 sys_getitimer data8 ia64_oldstat // 1120 data8 ia64_oldlstat data8 ia64_oldfstat data8 sys_vhangup data8 sys_lchown data8 sys_vm86 // 1125 data8 sys_wait4 data8 sys_sysinfo data8 sys_clone data8 sys_setdomainname data8 sys_newuname // 1130 data8 sys_adjtimex data8 ia64_create_module data8 sys_init_module data8 sys_delete_module data8 sys_get_kernel_syms // 1135 data8 sys_query_module data8 sys_quotactl data8 sys_bdflush data8 sys_sysfs data8 sys_personality // 1140 data8 ia64_ni_syscall // sys_afs_syscall data8 sys_setfsuid data8 sys_setfsgid data8 sys_getdents data8 sys_flock // 1145 data8 sys_readv data8 sys_writev data8 sys_pread data8 sys_pwrite data8 sys_sysctl // 1150 data8 sys_mmap data8 sys_munmap data8 sys_mlock data8 sys_mlockall data8 sys_mprotect // 1155 data8 sys_mremap data8 sys_msync data8 sys_munlock data8 sys_munlockall data8 sys_sched_getparam // 1160 data8 sys_sched_setparam data8 sys_sched_getscheduler data8 sys_sched_setscheduler data8 sys_sched_yield data8 sys_sched_get_priority_max // 1165 data8 sys_sched_get_priority_min data8 sys_sched_rr_get_interval data8 sys_nanosleep data8 sys_nfsservctl data8 sys_prctl // 1170 data8 sys_getpagesize data8 sys_mmap2 data8 sys_pciconfig_read data8 sys_pciconfig_write data8 sys_perfmonctl // 1175 data8 sys_sigaltstack data8 sys_rt_sigaction data8 sys_rt_sigpending data8 sys_rt_sigprocmask data8 sys_rt_sigqueueinfo // 1180 data8 sys_rt_sigreturn data8 sys_rt_sigsuspend data8 sys_rt_sigtimedwait data8 sys_getcwd data8 sys_capget // 1185 data8 sys_capset data8 sys_sendfile data8 sys_ni_syscall // sys_getpmsg (STREAMS) data8 sys_ni_syscall // sys_putpmsg (STREAMS) data8 sys_socket // 1190 data8 sys_bind data8 sys_connect data8 sys_listen data8 sys_accept data8 sys_getsockname // 1195 data8 sys_getpeername data8 sys_socketpair data8 sys_send data8 sys_sendto data8 sys_recv // 1200 data8 sys_recvfrom data8 sys_shutdown data8 sys_setsockopt data8 sys_getsockopt data8 sys_sendmsg // 1205 data8 sys_recvmsg data8 sys_pivot_root data8 sys_mincore data8 sys_madvise data8 sys_newstat // 1210 data8 sys_newlstat data8 sys_newfstat data8 sys_clone2 data8 sys_getdents64 data8 sys_getunwind // 1215 data8 sys_readahead data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1220 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1225 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 sys_tkill data8 ia64_ni_syscall // 1230 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1235 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1240 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1245 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1250 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1255 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1260 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1265 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1270 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall // 1275 data8 ia64_ni_syscall data8 ia64_ni_syscall data8 ia64_ni_syscall