about summary refs log tree commit diff
path: root/sysdeps/powerpc/powerpc64/sysdep.h
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/powerpc/powerpc64/sysdep.h')
-rw-r--r--sysdeps/powerpc/powerpc64/sysdep.h120
1 files changed, 114 insertions, 6 deletions
diff --git a/sysdeps/powerpc/powerpc64/sysdep.h b/sysdeps/powerpc/powerpc64/sysdep.h
index d557098898..f6a0619743 100644
--- a/sysdeps/powerpc/powerpc64/sysdep.h
+++ b/sysdeps/powerpc/powerpc64/sysdep.h
@@ -17,6 +17,7 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <sysdeps/powerpc/sysdep.h>
+#include <tls.h>
 
 #ifdef __ASSEMBLER__
 
@@ -263,10 +264,80 @@ LT_LABELSUFFIX(name,_name_end): ; \
   TRACEBACK_MASK(name,mask);	\
   END_2(name)
 
-#define DO_CALL(syscall) \
-    li 0,syscall; \
+/* We will allocate a new frame to save LR and the non-volatile register used to
+   read the TCB when checking for scv support on syscall code.  We actually just
+   need the minimum frame size plus room for 1 reg (8 bytes).  But the ABI
+   mandates stack frames should be aligned at 16 Bytes, so we end up allocating
+   a bit more space then what will actually be used.  */
+#define SCV_FRAME_SIZE (FRAME_MIN_SIZE+16)
+#define SCV_FRAME_NVOLREG_SAVE FRAME_MIN_SIZE
+
+/* Allocate frame and save register */
+#define NVOLREG_SAVE \
+    stdu r1,-SCV_FRAME_SIZE(r1); \
+    std r31,SCV_FRAME_NVOLREG_SAVE(r1); \
+    cfi_adjust_cfa_offset(SCV_FRAME_SIZE);
+
+/* Restore register and destroy frame */
+#define NVOLREG_RESTORE	\
+    ld r31,SCV_FRAME_NVOLREG_SAVE(r1); \
+    addi r1,r1,SCV_FRAME_SIZE; \
+    cfi_adjust_cfa_offset(-SCV_FRAME_SIZE);
+
+/* Check PPC_FEATURE2_SCV bit from hwcap2 in the TCB.  If it is not set, scv is
+   not available, then go to JUMPFALSE (label given by the macro's caller).  We
+   save the value we read from the TCB in a non-volatile register so we can
+   reuse it later when exiting from the syscall in PSEUDO_RET.  Note that for
+   the static case we need an extra check to guarantee the thread pointer has
+   already been initialized, otherwise we may try to access an invalid address
+   if a syscall is called before the TLS has been setup.  */
+    .macro CHECK_SCV_SUPPORT REG JUMPFALSE
+
+#ifndef SHARED
+    /* Check if thread pointer has already been setup.  */
+    cmpdi r13,0
+    beq \JUMPFALSE
+#endif
+
+    /* Read PPC_FEATURE2_SCV from TCB and store it in REG */
+    ld \REG,TCB_HWCAP(PT_THREAD_POINTER)
+    andis. \REG,\REG,PPC_FEATURE2_SCV>>16
+
+    beq \JUMPFALSE
+    .endm
+
+#if IS_IN(rtld)
+# define DO_CALL(syscall) \
+    li r0,syscall; \
+    DO_CALL_SC
+#else
+/* Before doing the syscall, check if we can use scv.  scv is supported by P9
+   and later with Linux v5.9 and later.  If so, use it.  Otherwise, fallback to
+   sc.  We use a non-volatile register to save hwcap2 from the TCB, so we need
+   to save its content beforehand.  */
+# define DO_CALL(syscall) \
+    li r0,syscall; \
+    NVOLREG_SAVE; \
+    CHECK_SCV_SUPPORT r31 0f; \
+    DO_CALL_SCV; \
+    b 1f; \
+0:  DO_CALL_SC; \
+1:
+#endif /* IS_IN(rtld) */
+
+/* DO_CALL_SC and DO_CALL_SCV expect the syscall number to be in r0.  */
+#define DO_CALL_SC \
     sc
 
+#define DO_CALL_SCV \
+    mflr r9; \
+    std r9,FRAME_LR_SAVE(r1); \
+    cfi_offset(lr,FRAME_LR_SAVE); \
+    scv 0; \
+    ld r9,FRAME_LR_SAVE(r1); \
+    mtlr r9; \
+    cfi_restore(lr);
+
 /* ppc64 is always PIC */
 #undef JUMPTARGET
 #define JUMPTARGET(name) FUNC_LABEL(name)
@@ -304,9 +375,32 @@ LT_LABELSUFFIX(name,_name_end): ; \
     .endif
 #endif
 
-#define PSEUDO_RET \
-    bnslr+; \
+#if IS_IN(rtld)
+# define PSEUDO_RET \
+    RET_SC; \
     TAIL_CALL_SYSCALL_ERROR
+#else
+/* This should only be called after a DO_CALL.  In such cases, r31 contains the
+   value of PPC_FEATURE2_SCV read from hwcap2 by CHECK_SCV_SUPPORT.  If it is
+   set, we know we have entered the kernel using scv, so handle the return code
+   accordingly.  */
+# define PSEUDO_RET \
+    cmpdi cr5,r31,0; \
+    NVOLREG_RESTORE; \
+    beq cr5,0f; \
+    RET_SCV; \
+    b 1f; \
+0:  RET_SC; \
+1:  TAIL_CALL_SYSCALL_ERROR
+#endif
+
+#define RET_SCV \
+    cmpdi r3,0; \
+    bgelr+; \
+    neg r3,r3;
+
+#define RET_SC \
+    bnslr+;
 
 #define ret PSEUDO_RET
 
@@ -319,8 +413,15 @@ LT_LABELSUFFIX(name,_name_end): ; \
   ENTRY (name);						\
   DO_CALL (SYS_ify (syscall_name))
 
-#define PSEUDO_RET_NOERRNO \
+#if IS_IN(rtld)
+# define PSEUDO_RET_NOERRNO \
     blr
+#else
+/* This should only be called after a DO_CALL.  */
+# define PSEUDO_RET_NOERRNO \
+    NVOLREG_RESTORE; \
+    blr
+#endif /* IS_IN(rtld) */
 
 #define ret_NOERRNO PSEUDO_RET_NOERRNO
 
@@ -333,8 +434,15 @@ LT_LABELSUFFIX(name,_name_end): ; \
   ENTRY (name);						\
   DO_CALL (SYS_ify (syscall_name))
 
-#define PSEUDO_RET_ERRVAL \
+#if IS_IN(rtld)
+# define PSEUDO_RET_ERRVAL \
+    blr
+#else
+/* This should only be called after a DO_CALL.  */
+# define PSEUDO_RET_ERRVAL \
+    NVOLREG_RESTORE; \
     blr
+#endif /* IS_IN(rtld) */
 
 #define ret_ERRVAL PSEUDO_RET_ERRVAL