aboutsummaryrefslogtreecommitdiff
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 d557098..f6a0619 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