/*--------------------------------------------------------------------*/ /*--- Support for doing system calls. syscall-ppc64-aix5.S ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2006-2010 OpenWorks LLP info@open-works.co.uk Derived from Paul Mackerras' implementation of same for ppc32-linux in syscall-ppc32-linux.S. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ #if defined(VGP_ppc64_aix5) #include "pub_core_basics_asm.h" #include "libvex_guest_offsets.h" /* kludge: from include/vki/vki-ppc64-aix5.h */ #define VKI_SIG_SETMASK 2 /*----------------------------------------------------------------*/ /* Perform a syscall for the client. This will run a syscall with the client's specific per-thread signal mask. The structure of this function is such that, if the syscall is interrupted by a signal, we can determine exactly what execution state we were in with respect to the execution of the syscall by examining the value of NIP in the signal handler. This means that we can always do the appropriate thing to precisely emulate the kernel's signal/syscall interactions. The syscall number is taken from the argument, even though it should also be in R2 in guest_state. The syscall result is written back to R3 and R4 in the guest state on completion. Returns 0 if the syscall was successfully called (even if the syscall itself failed), or a nonzero error code in the lowest 8 bits if one of the sigprocmasks failed (there's no way to determine which one failed). And there's no obvious way to recover from that either, but nevertheless we want to know. VG_(fixup_guest_state_after_syscall_interrupted) does the thread state fixup in the case where we were interrupted by a signal. Prototype: UWord ML_(do_syscall_for_client_WRK)( Int syscallno, // r3 void* guest_state, // r4 const vki_sigset_t *sysmask, // r5 const vki_sigset_t *postmask, // r6 Int sigsetSzB, // r7 Int __nr_sigprocmask) // r8 */ .file "syscall-ppc64-aix6.S" .toc .csect .text[PR] .align 2 .globl ML_(do_syscall_for_client_WRK) .globl .ML_(do_syscall_for_client_WRK) .csect ML_(do_syscall_for_client_WRK)[DS] ML_(do_syscall_for_client_WRK): .llong .ML_(do_syscall_for_client_WRK), TOC[tc0], 0 .csect .text[PR] .ML_(do_syscall_for_client_WRK): /* make a stack frame */ stdu 1,-1024(1) std 31,512(1) std 30,520(1) std 29,528(1) std 28,536(1) std 27,544(1) std 26,552(1) mflr 26 std 26,560(1) std 2,568(1) mr 31,3 /* syscall number */ mr 30,4 /* guest_state */ mr 29,6 /* postmask */ mr 28,7 /* sigsetSzB */ mr 27,8 /* __nr_sigprocmask */ Lvg1: /* Even though we can't take a signal until the sigprocmask completes, start the range early. If PC is in the range [1,2), the syscall hasn't been started yet */ /* set the signal mask for doing the system call */ /* set up for sigprocmask(SIG_SETMASK, sysmask, postmask) */ mr 2,8 li 3,VKI_SIG_SETMASK mr 4,5 mr 5,6 mr 6,7 /* sigsetSzB -- needed on AIX ? */ /* actually do the sigprocmask */ crorc 6,6,6 .long 0x48000005 /* bl here+4 */ mflr 26 addi 26,26,16 mtlr 26 sc /* did it fail? (assuming r3 == 0 for success) */ cmpdi 0,3,0 bne 0,Lvg7 /* load up syscall args from the threadstate */ ld 3,OFFSET_ppc64_GPR3(30) ld 4,OFFSET_ppc64_GPR4(30) ld 5,OFFSET_ppc64_GPR5(30) ld 6,OFFSET_ppc64_GPR6(30) ld 7,OFFSET_ppc64_GPR7(30) ld 8,OFFSET_ppc64_GPR8(30) ld 9,OFFSET_ppc64_GPR9(30) ld 10,OFFSET_ppc64_GPR10(30) mr 2,31 /* syscall number */ crorc 6,6,6 .long 0x48000005 /* bl here+4 */ mflr 26 addi 26,26,16 mtlr 26 /* If PC is in the range [2,2], then the syscall was either just about to start, or was interrupted and the kernel was restarting it. */ Lvg2: sc /* do the syscall */ /* In the range [3, 4), the syscall result is in r3/r4, but hasn't been committed to R3/R4. */ /* put the result back in the threadstate */ Lvg3: std 3,OFFSET_ppc64_GPR3(30) /* gst->GPR3 = res */ std 4,OFFSET_ppc64_GPR4(30) /* gst->GPR4 = err */ /* Block signals again. If PC is in [4,5), then the syscall is complete and we needn't worry about it. */ /* set up for sigprocmask(SIG_SETMASK, postmask, NULL) */ Lvg4: mr 2,27 li 3,VKI_SIG_SETMASK mr 4,29 li 5,0 mr 6,28 /* sigsetSzB -- needed on AIX ? */ /* actually do the sigprocmask */ crorc 6,6,6 .long 0x48000005 /* bl here+4 */ mflr 26 addi 26,26,16 mtlr 26 sc /* did it fail? (assuming r3 == 0 for success) */ cmpdi 0,3,0 bne 0,Lvg7 /* now safe from signals */ li 3,0 /* SUCCESS */ /* pop off stack frame */ Lvg5: ld 2,568(1) ld 26,560(1) mtlr 26 ld 26,552(1) ld 27,544(1) ld 28,536(1) ld 29,528(1) ld 30,520(1) ld 31,512(1) addi 1,1,1024 blr /* failure: return 0x8000 | error code */ Lvg7: mr 3,4 ori 3,3,0x8000 /* FAILURE -- ensure return value is nonzero */ b Lvg5 /* export the ranges so that VG_(fixup_guest_state_after_syscall_interrupted) can do the right thing */ .csect .data[RW],3 .align 2 .globl ML_(blksys_setup) .globl ML_(blksys_restart) .globl ML_(blksys_complete) .globl ML_(blksys_committed) .globl ML_(blksys_finished) ML_(blksys_setup): .llong Lvg1 ML_(blksys_restart): .llong Lvg2 ML_(blksys_complete): .llong Lvg3 ML_(blksys_committed): .llong Lvg4 ML_(blksys_finished): .llong Lvg5 #endif // defined(VGP_ppc64_aix5) /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/