summaryrefslogtreecommitdiff
path: root/sysdeps/unix
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix')
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S33
1 files changed, 30 insertions, 3 deletions
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S b/sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S
index fc496fa671..247e0de68c 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/clone.S
@@ -38,9 +38,11 @@ ENTRY (__clone)
beq- cr0,L(badargs)
/* Save some regs in the "red zone". */
+ std r28,-32(r1)
std r29,-24(r1)
std r30,-16(r1)
std r31,-8(r1)
+ cfi_offset(r28,-32)
cfi_offset(r29,-24)
cfi_offset(r30,-16)
cfi_offset(r31,-8)
@@ -69,11 +71,26 @@ ENTRY (__clone)
/* Do the call. */
li r0,SYS_ify(clone)
- DO_CALL_SC
+ CHECK_SCV_SUPPORT r28 0f
+ /* This is equivalent to DO_CALL_SCV, but we cannot use the macro here
+ because it uses CFI directives and we just called cfi_endproc. */
+ mflr r9
+ std r9,FRAME_LR_SAVE(r1)
+ scv 0
+ ld r9,FRAME_LR_SAVE(r1)
+ mtlr r9
+
+ /* Check for child process. */
+ /* When using scv, error is indicated by negative r3. */
+ cmpdi cr1,r3,0
+ b 1f
+0: DO_CALL_SC
/* Check for child process. */
+ /* With sc, error is indicated by cr0.SO. */
cmpdi cr1,r3,0
crandc cr1*4+eq,cr1*4+eq,cr0*4+so
+1:
bne- cr1,L(parent) /* The '-' is to minimise the race. */
std r2,FRAME_TOC_SAVE(r1)
@@ -95,19 +112,29 @@ L(badargs):
TAIL_CALL_SYSCALL_ERROR
L(parent):
+ /* Check if scv is available. */
+ cmpdi cr1,r28,0
+
/* Parent. Restore registers & return. */
+ cfi_offset(r28,-32)
cfi_offset(r29,-24)
cfi_offset(r30,-16)
cfi_offset(r31,-8)
+ ld r28,-32(r1)
ld r29,-24(r1)
ld r30,-16(r1)
ld r31,-8(r1)
+ cfi_restore(r28)
cfi_restore(r29)
cfi_restore(r30)
cfi_restore(r31)
- RET_SC
- TAIL_CALL_SYSCALL_ERROR
+ beq cr1,0f
+ RET_SCV
+ b 1f
+0: RET_SC
+1: TAIL_CALL_SYSCALL_ERROR
+
END (__clone)