git: 3bae1cd68a2a - main - linux(4): Implement futex_op for arm64.

From: Dmitry Chagin <dchagin_at_FreeBSD.org>
Date: Sun, 15 May 2022 17:50:27 UTC
The branch main has been updated by dchagin:

URL: https://cgit.FreeBSD.org/src/commit/?id=3bae1cd68a2ac3e2c37d54cf550e01257dfb1a04

commit 3bae1cd68a2ac3e2c37d54cf550e01257dfb1a04
Author:     Dmitry Chagin <dchagin@FreeBSD.org>
AuthorDate: 2022-05-15 17:49:42 +0000
Commit:     Dmitry Chagin <dchagin@FreeBSD.org>
CommitDate: 2022-05-15 17:49:42 +0000

    linux(4): Implement futex_op for arm64.
    
    It's mostly modeled like the Linux does.
    
    Differential revision:  https://reviews.freebsd.org/D35154
    MFC after:              2 weeks
---
 sys/arm64/linux/linux_support.s | 130 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 123 insertions(+), 7 deletions(-)

diff --git a/sys/arm64/linux/linux_support.s b/sys/arm64/linux/linux_support.s
index 1048e9579627..933930b5096f 100644
--- a/sys/arm64/linux/linux_support.s
+++ b/sys/arm64/linux/linux_support.s
@@ -2,6 +2,7 @@
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
  * Copyright (C) 2018 Turing Robotic Industries Inc.
+ * Copyright (C) 2022 Dmitry Chagin <dchagin@FreeBSD.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,36 +28,151 @@
  * $FreeBSD$
  */
 
-#include "linux_assym.h"
 #include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+#include <machine/param.h>
+#include <machine/vmparam.h>
+
+#include <sys/errno.h>
 
 #include "assym.inc"
 
+.macro check_user_access user_arg, limit, bad_addr_func
+	ldr	x7, =(\limit)
+	cmp	x\user_arg, x7
+	b.cs	\bad_addr_func
+.endm
+
+futex_fault:
+	SET_FAULT_HANDLER(xzr, x1)
+	EXIT_USER_ACCESS_CHECK(w0, x1)
+futex_fault_nopcb:
+	mov	x0, #EFAULT
+	ret
+
+#define	LINUX_FUTEX_MAX_LOOPS	128
+
 /*
- * LINUXTODO: implement futex_*
+ * int oparg, uint32_t *uaddr, int *oldval
+ *
+ * Return 0 on success, errno on failure,
+ * EAGAIN is returned if LL/SC operation fails.
+ *
+ * XXX. VM_MAXUSER_ADDRESS is not applicable here, should be replaced
+ * by something like LINUX_SHAREDPAGE.
  */
 
+/* (int *)uaddr2 = oparg */
 ENTRY(futex_xchgl)
-	brk #0
+	check_user_access 1, (VM_MAXUSER_ADDRESS-3), futex_fault_nopcb
+	adr	x9, futex_fault		/* Load the fault handler */
+	SET_FAULT_HANDLER(x9, x4)	/* And set it */
+	ENTER_USER_ACCESS(w9, x4)
+	mov	w5, #LINUX_FUTEX_MAX_LOOPS
+	prfm	pstl1strm, [x1]
+	mov	w6, w0			/* Save oparg */
+1:	ldxr	w4, [x1]		/* Load oldval from uaddr */
+	stlxr	w0, w6, [x1]		/* Store oparg to uaddr */
+	cbz	w0, 3f			/* Exit on success */
+	sub	w5, w5, w0		/* Dec loop counter, w0 is 1 */
+	cbnz	w5, 1b			/* Loop */
+	mov	x0, #EAGAIN		/* Store of newval failed */
+3:	dmb	ish
+	EXIT_USER_ACCESS(w9)
+	SET_FAULT_HANDLER(xzr, x9)	/* Reset the fault handler */
+	str w4, [x2]			/* Store oldval */
 	ret
 END(futex_xchgl)
 
+/* (int *)uaddr2 += oparg */
 ENTRY(futex_addl)
-	brk #0
+	check_user_access 1, (VM_MAXUSER_ADDRESS-3), futex_fault_nopcb
+	adr	x9, futex_fault
+	SET_FAULT_HANDLER(x9, x4)
+	ENTER_USER_ACCESS(w9, x4)
+	mov	w5, #LINUX_FUTEX_MAX_LOOPS
+	prfm	pstl1strm, [x1]
+	mov	w6, w0
+1:	ldxr	w4, [x1]
+	add	w3, w4, w6		/* oldval + oparg */
+	stlxr	w0, w3, [x1]
+	cbz	w0, 3f
+	sub	w5, w5, w0
+	cbnz	w5, 1b
+	mov	x0, #EAGAIN
+3:	dmb	ish
+	EXIT_USER_ACCESS(w9)
+	SET_FAULT_HANDLER(xzr, x9)
+	str w4, [x2]
 	ret
 END(futex_addl)
 
+/* (int *)uaddr2 |= oparg */
 ENTRY(futex_orl)
-	brk #0
+	check_user_access 1, (VM_MAXUSER_ADDRESS-3), futex_fault_nopcb
+	adr	x9, futex_fault
+	SET_FAULT_HANDLER(x9, x4)
+	ENTER_USER_ACCESS(w9, x4)
+	mov	w5, #LINUX_FUTEX_MAX_LOOPS
+	prfm	pstl1strm, [x1]
+	mov	w6, w0
+1:	ldxr	w4, [x1]
+	orr	w3, w4, w6		/* oldavl |= oparg */
+	stlxr	w0, w3, [x1]
+	cbz	w0, 3f
+	sub	w5, w5, w0
+	cbnz	w5, 1b
+	mov	x0, #EAGAIN
+3:	dmb	ish
+	EXIT_USER_ACCESS(w9)
+	SET_FAULT_HANDLER(xzr, x9)
+	str w4, [x2]
 	ret
 END(futex_orl)
 
+/* (int *)uaddr2 &= oparg */
 ENTRY(futex_andl)
-	brk #0
+	check_user_access 1, (VM_MAXUSER_ADDRESS-3), futex_fault_nopcb
+	adr	x9, futex_fault
+	SET_FAULT_HANDLER(x9, x4)
+	ENTER_USER_ACCESS(w9, x4)
+	mov	w5, #LINUX_FUTEX_MAX_LOOPS
+	prfm	pstl1strm, [x1]
+	mov	w6, w0
+1:	ldxr	w4, [x1]
+	and	w3, w4, w6		/* oldval &= oparg */
+	stlxr	w0, w3, [x1]
+	cbz	w0, 3f
+	sub	w5, w5, w0
+	cbnz	w5, 1b
+	mov	x0, #EAGAIN
+3:	dmb	ish
+	EXIT_USER_ACCESS(w9)
+	SET_FAULT_HANDLER(xzr, x9)
+	str w4, [x2]
 	ret
 END(futex_andl)
 
+/* (int *)uaddr2 ^= oparg */
 ENTRY(futex_xorl)
-	brk #0
+	check_user_access 1, (VM_MAXUSER_ADDRESS-3), futex_fault_nopcb
+	adr	x9, futex_fault
+	SET_FAULT_HANDLER(x9, x4)
+	ENTER_USER_ACCESS(w9, x4)
+	mov	w5, #LINUX_FUTEX_MAX_LOOPS
+	prfm	pstl1strm, [x1]
+	mov	w6, w0
+1:	ldxr	w4, [x1]
+	eor	w3, w4, w6		/* oldval ^= oparg */
+	stlxr	w0, w3, [x1]
+	cbz	w0, 3f
+	sub	w5, w5, w0
+	cbnz	w5, 1b
+	mov	x0, #EAGAIN
+3:	dmb	ish
+	EXIT_USER_ACCESS(w9)
+	SET_FAULT_HANDLER(xzr, x9)
+	str w4, [x2]
 	ret
 END(futex_xorl)