Blame sysdeps/unix/sysv/linux/powerpc/powerpc64/makecontext.S

Packit 6c4009
/* Create new context.
Packit 6c4009
   Copyright (C) 2002-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <sysdep.h>
Packit 6c4009
Packit 6c4009
#define __ASSEMBLY__
Packit 6c4009
#include <asm/ptrace.h>
Packit 6c4009
#include "ucontext_i.h"
Packit 6c4009
#include <asm/errno.h>
Packit 6c4009
Packit 6c4009
ENTRY (__makecontext)
Packit 6c4009
	CALL_MCOUNT 3
Packit 6c4009
  /* Save parameters into the parameter save area of callers frame.  */
Packit 6c4009
  std  r3,FRAME_PARM_SAVE+0(r1) /* ucontext_t *ucp  */
Packit 6c4009
  std  r4,FRAME_PARM_SAVE+8(r1) /* void (*func)(void)  */
Packit 6c4009
  std  r5,FRAME_PARM_SAVE+16(r1) /* int argc  */
Packit 6c4009
  std  r6,FRAME_PARM_SAVE+24(r1) /* ... */
Packit 6c4009
  std  r7,FRAME_PARM_SAVE+32(r1)
Packit 6c4009
  std  r8,FRAME_PARM_SAVE+40(r1)
Packit 6c4009
  std  r9,FRAME_PARM_SAVE+48(r1)
Packit 6c4009
  std  r10,FRAME_PARM_SAVE+56(r1)
Packit 6c4009
  mflr  r0
Packit 6c4009
  /* Get the address of the target functions first parameter.  */
Packit 6c4009
  addi  r6,r1,FRAME_PARM_SAVE+24
Packit 6c4009
  std   r0,FRAME_LR_SAVE(r1)
Packit 6c4009
  cfi_offset (lr, FRAME_LR_SAVE)
Packit 6c4009
  stdu  r1,-128(r1)
Packit 6c4009
  cfi_adjust_cfa_offset (128)
Packit 6c4009
Packit 6c4009
  /* Get the ucontexts stack pointer and size.  Compute the top of stack
Packit 6c4009
     and round down to a quadword boundary.  Then stack a dummy frame
Packit 6c4009
     with a null back chain.  We store the context pointer in the frames
Packit 6c4009
     "compiler double word" field so we can recover if is the function
Packit 6c4009
     returns.  Finally save the callers link register and TOC pointer
Packit 6c4009
     into this frame so the debugger can display a backtrace.
Packit 6c4009
  */
Packit 6c4009
  ld    r7,UCONTEXT_STACK_SP(r3)
Packit 6c4009
  ld    r0,UCONTEXT_STACK_SIZE(r3)
Packit 6c4009
  add   r7,r7,r0
Packit 6c4009
  clrrdi  r7,r7,4
Packit 6c4009
  li    r0,0
Packit 6c4009
  stdu  r0,-64(r7)
Packit 6c4009
  std   r3,FRAME_PARM_SAVE(r7) /* Store context in dummy parm1.  */
Packit 6c4009
  mflr  r0
Packit 6c4009
  std   r2,FRAME_TOC_SAVE(r7)  /* Store the TOC pointer for later.  */
Packit 6c4009
  std   r0,FRAME_LR_SAVE(r7)
Packit 6c4009
Packit 6c4009
  /* Now we need to stack another frame to hold the parameter save area
Packit 6c4009
     for the function.  We need to allocate a frame with the minimum 48
Packit 6c4009
     byte header and 8 parameter register.  However if there are more
Packit 6c4009
     than 8 parameters addition space is need to hold all the parameters.
Packit 6c4009
     The total size it rounded up to a quadword multiple then a frame is
Packit 6c4009
     stacked.  This address is stored in the ucontext as GPR 1.  */
Packit 6c4009
Packit 6c4009
  cmpdi cr1,r5,8
Packit 6c4009
  sldi  r8,r5,3
Packit 6c4009
  bgt   cr1,L(gt8)
Packit 6c4009
  li    r8,64
Packit 6c4009
L(gt8):
Packit 6c4009
  addi  r8,r8,FRAME_PARM_SAVE+8 /* Add header plus rounding factor.  */
Packit 6c4009
  clrrdi  r8,r8,4  /* Round down to quadword.  */
Packit 6c4009
Packit 6c4009
  subf  r8,r8,r7
Packit 6c4009
  std   r7,0(r8)   /* Stack the frame.  */
Packit 6c4009
  std   r8,(SIGCONTEXT_GP_REGS+(PT_R1*8))(r3)
Packit 6c4009
Packit 6c4009
  /* Now we need to copy the target functions parameters.  The functions
Packit 6c4009
     parameters are saved in the parameter save area.  We skip over the
Packit 6c4009
     first three parameters and copy up to 8 double word into the
Packit 6c4009
     SIGCONTEXT_GP_REGS starting with R3.  If there are more than 8
Packit 6c4009
     parameters then doublewords 8-N are copied into the parameter
Packit 6c4009
     save area of the context frame.  */
Packit 6c4009
  cmpdi r5,0
Packit 6c4009
  beq   L(noparms)
Packit 6c4009
  mr    r0,r5
Packit 6c4009
  ble   cr1,L(le8)
Packit 6c4009
  li    r0,8
Packit 6c4009
L(le8):
Packit 6c4009
  mtctr r0
Packit 6c4009
  addi  r7,r6,-8
Packit 6c4009
  addi  r9,r3,(SIGCONTEXT_GP_REGS+(PT_R3*8)-8)
Packit 6c4009
L(parmloop2):
Packit 6c4009
  ldu   r0,8(r7)
Packit 6c4009
  stdu  r0,8(r9)
Packit 6c4009
  bdnz  L(parmloop2)
Packit 6c4009
Packit 6c4009
  addi  r0,r5,-8
Packit 6c4009
  ble   cr1,L(noparms)
Packit 6c4009
  mtctr r0
Packit 6c4009
  addi  r9,r8,FRAME_PARM_SAVE+64-8
Packit 6c4009
L(parmloop):
Packit 6c4009
  ldu   r0,8(r7)
Packit 6c4009
  stdu  r0,8(r9)
Packit 6c4009
  bdnz  L(parmloop)
Packit 6c4009
Packit 6c4009
L(noparms):
Packit 6c4009
Packit 6c4009
#if _CALL_ELF != 2
Packit 6c4009
  /* Load the function address and TOC from the function descriptor
Packit 6c4009
     and store them in the ucontext as NIP and r2.  Store the 3rd
Packit 6c4009
     field of the function descriptor into the ucontext as r11 in case
Packit 6c4009
     the calling language needs the "environment pointer".  */
Packit 6c4009
  ld    r0,0(r4)
Packit 6c4009
  ld    r10,8(r4);
Packit 6c4009
  ld    r9,16(r4);
Packit 6c4009
  std   r0,(SIGCONTEXT_GP_REGS+(PT_NIP*8))(r3)
Packit 6c4009
  std   r10,(SIGCONTEXT_GP_REGS+(PT_R2*8))(r3)
Packit 6c4009
  std   r9,(SIGCONTEXT_GP_REGS+(PT_R11*8))(r3)
Packit 6c4009
#else
Packit 6c4009
  /* In the ELFv2 ABI, the function pointer is already the address.
Packit 6c4009
     Store it as NIP and r12 as required by the ABI.  */
Packit 6c4009
  std   r4,(SIGCONTEXT_GP_REGS+(PT_NIP*8))(r3)
Packit 6c4009
  std   r4,(SIGCONTEXT_GP_REGS+(PT_R12*8))(r3)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* If the target function returns we need to do some cleanup.  We use a
Packit 6c4009
     code trick to get the address of our cleanup function into the link
Packit 6c4009
     register.  Do not add any code between here and L(exitcode).
Packit 6c4009
     Use this conditional form of branch and link to avoid destroying
Packit 6c4009
     the cpu link stack used to predict blr return addresses.  */
Packit 6c4009
  bcl	20,31,L(gotexitcodeaddr);
Packit 6c4009
Packit 6c4009
  /* End FDE now, because while executing on the context's stack
Packit 6c4009
     the unwind info would be wrong otherwise.  */
Packit 6c4009
  cfi_endproc
Packit 6c4009
Packit 6c4009
	/* This is the helper code which gets called if a function which
Packit 6c4009
	   is registered with 'makecontext' returns.  In this case we
Packit 6c4009
	   have to install the context listed in the uc_link element of
Packit 6c4009
	   the context 'makecontext' manipulated at the time of the
Packit 6c4009
	   'makecontext' call.  If the pointer is NULL the process must
Packit 6c4009
	   terminate.  */
Packit 6c4009
L(exitcode):
Packit 6c4009
	/* Recover the ucontext and TOC from the dummy frame.  */
Packit 6c4009
	ld    r1,FRAME_BACKCHAIN(r1)  /* Unstack the parameter save area frame.  */
Packit 6c4009
	ld    r3,FRAME_PARM_SAVE(r1)
Packit 6c4009
	ld    r2,FRAME_TOC_SAVE(r1)
Packit 6c4009
	ld    r3,UCONTEXT_LINK(r3)  /* Load the resume context.  */
Packit 6c4009
	cmpdi r3,0
Packit 6c4009
	beq   L(do_exit)
Packit 6c4009
	bl    JUMPTARGET(__setcontext)
Packit 6c4009
	nop
Packit 6c4009
	/* If setcontext returns (which can happen if the syscall fails) we will
Packit 6c4009
	   exit the program with error status (-1).  */
Packit 6c4009
	li    r3,-1
Packit 6c4009
L(do_exit):
Packit 6c4009
#ifdef SHARED
Packit 6c4009
	b     JUMPTARGET(__GI_exit);
Packit 6c4009
#else
Packit 6c4009
	b     JUMPTARGET(exit);
Packit 6c4009
	nop
Packit 6c4009
#endif
Packit 6c4009
	b    L(do_exit)
Packit 6c4009
Packit 6c4009
  /* Re-establish FDE for the rest of the actual makecontext routine.  */
Packit 6c4009
  cfi_startproc
Packit 6c4009
  cfi_offset (lr, FRAME_LR_SAVE)
Packit 6c4009
  cfi_adjust_cfa_offset (128)
Packit 6c4009
Packit 6c4009
  /* The address of the exit code is in the link register.  Store the lr
Packit 6c4009
     in the ucontext as LNK so the target function will return to our
Packit 6c4009
     exit code.  */
Packit 6c4009
L(gotexitcodeaddr):
Packit 6c4009
  mflr  r0
Packit 6c4009
  std   r0,(SIGCONTEXT_GP_REGS+(PT_LNK*8))(r3)
Packit 6c4009
  ld    r0,128+FRAME_LR_SAVE(r1)
Packit 6c4009
  addi  r1,r1,128
Packit 6c4009
  mtlr  r0
Packit 6c4009
  blr
Packit 6c4009
END(__makecontext)
Packit 6c4009
Packit 6c4009
weak_alias (__makecontext, makecontext)