git: ccb59683b983 - main - arm64: add tests for swp/swpb emulation

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Mon, 15 May 2023 15:42:24 UTC
The branch main has been updated by kevans:

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

commit ccb59683b98360afaf5b5bb641a68fea22c68d0b
Author:     Kyle Evans <kevans@FreeBSD.org>
AuthorDate: 2023-05-15 15:42:16 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2023-05-15 15:42:16 +0000

    arm64: add tests for swp/swpb emulation
    
    One test is suitable to be hooked up to the build, so I've done this
    here.  The other test lives in tools/regression because failure is a
    bit more subjective -- generally, one runs it for some unbounded amount
    of time and observe if it eventually exits because two threads acquired
    the same mutex.
    
    Reviewed by:    imp, mmel
    Sponsored by:   Stormshield
    Sponsored by:   Klara, Inc.
    Differential Revision:  https://reviews.freebsd.org/D39668
---
 etc/mtree/BSD.tests.dist                          |   2 +
 tests/sys/Makefile                                |   1 +
 tests/sys/compat32/Makefile                       |   6 +
 tests/sys/compat32/Makefile.inc                   |   4 +
 tests/sys/compat32/aarch64/Makefile               |  24 ++
 tests/sys/compat32/aarch64/common.sh              |   9 +
 tests/sys/compat32/aarch64/swp_cond_test.sh       |  14 +
 tests/sys/compat32/aarch64/swp_cond_test_impl.S   | 413 ++++++++++++++++++++++
 tests/sys/compat32/aarch64/swp_test.sh            |  14 +
 tests/sys/compat32/aarch64/swp_test_impl.S        | 216 +++++++++++
 tools/regression/compat32/aarch64/Makefile        |   4 +
 tools/regression/compat32/aarch64/swp_test_impl.S | 410 +++++++++++++++++++++
 12 files changed, 1117 insertions(+)

diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
index 5d99653100df..77c07960f938 100644
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -750,6 +750,8 @@
                 ..
             ..
         ..
+        compat32
+        ..
         devrandom
         ..
         dtrace
diff --git a/tests/sys/Makefile b/tests/sys/Makefile
index 8f83445f9d91..3e9a30105a1d 100644
--- a/tests/sys/Makefile
+++ b/tests/sys/Makefile
@@ -10,6 +10,7 @@ TESTS_SUBDIRS+=		${_audit}
 TESTS_SUBDIRS+=		auditpipe
 TESTS_SUBDIRS+=		capsicum
 TESTS_SUBDIRS+=		${_cddl}
+TESTS_SUBDIRS+=		compat32
 TESTS_SUBDIRS+=		devrandom
 TESTS_SUBDIRS+=		fifo
 TESTS_SUBDIRS+=		file
diff --git a/tests/sys/compat32/Makefile b/tests/sys/compat32/Makefile
new file mode 100644
index 000000000000..31834de16246
--- /dev/null
+++ b/tests/sys/compat32/Makefile
@@ -0,0 +1,6 @@
+
+.if exists(${.CURDIR}/${MACHINE_ARCH})
+SUBDIR+=	${MACHINE_ARCH}
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/tests/sys/compat32/Makefile.inc b/tests/sys/compat32/Makefile.inc
new file mode 100644
index 000000000000..0220ac0431e9
--- /dev/null
+++ b/tests/sys/compat32/Makefile.inc
@@ -0,0 +1,4 @@
+
+TESTSDIR=		${TESTSBASE}/sys/compat32
+
+.include "../Makefile.inc"
diff --git a/tests/sys/compat32/aarch64/Makefile b/tests/sys/compat32/aarch64/Makefile
new file mode 100644
index 000000000000..716182b15d9c
--- /dev/null
+++ b/tests/sys/compat32/aarch64/Makefile
@@ -0,0 +1,24 @@
+PACKAGE=	tests
+FILESGROUPS+=	asmprogs
+
+ACFLAGS= -target armv7-unknown-freebsd${OS_REVISION} -nostdlib -Wl,-e -Wl,main -static
+
+TAP_TESTS_SH+=	swp_cond_test
+TAP_TESTS_SH+=	swp_test
+${PACKAGE}FILES+=	common.sh
+
+# Each test will individually respect the compat.arm.emul_swp
+# sysctl upon entry.
+TEST_METADATA.swp_cond_test+=	is_exclusive=true
+TEST_METADATA.swp_test+=	is_exclusive=true
+
+asmprogsMODE=		0755
+asmprogs+=	swp_cond_test_impl swp_test_impl
+asmprogsDIR=	${TESTSDIR}
+
+.for aprog in ${asmprogs}
+${aprog}: ${aprog}.S
+	${CC} ${ACFLAGS} -o ${.TARGET} ${.ALLSRC}
+.endfor
+
+.include <bsd.test.mk>
diff --git a/tests/sys/compat32/aarch64/common.sh b/tests/sys/compat32/aarch64/common.sh
new file mode 100644
index 000000000000..f8b02e8ac157
--- /dev/null
+++ b/tests/sys/compat32/aarch64/common.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+if ! sysctl -n kern.features.compat_freebsd_32bit >/dev/null 2>&1; then
+	echo "1..0 # Skipped: Kernel not built with COMPAT_FREEBSD32"
+	exit 0
+elif ! sysctl -n kern.supported_archs | grep -q '\<armv7\>'; then
+	echo "1..0 # Skipped: 32-bit ARM not supported on this hardware"
+	exit 0
+fi
diff --git a/tests/sys/compat32/aarch64/swp_cond_test.sh b/tests/sys/compat32/aarch64/swp_cond_test.sh
new file mode 100644
index 000000000000..8edfa43b3e7e
--- /dev/null
+++ b/tests/sys/compat32/aarch64/swp_cond_test.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+scriptdir=$(dirname $(realpath "$0"))
+
+. ${scriptdir}/common.sh
+
+# Ensure emul_swp is enabled just for this test; we'll turn it back off if
+# it wasn't enabled before the test.
+emul_swpval=$(sysctl -n compat.arm.emul_swp)
+sysctl compat.arm.emul_swp=1 >/dev/null
+${scriptdir}/swp_test_impl
+if [ "$emul_swpval" -ne 1 ]; then
+	sysctl compat.arm.emul_swp="$emul_swpval" >/dev/null
+fi
diff --git a/tests/sys/compat32/aarch64/swp_cond_test_impl.S b/tests/sys/compat32/aarch64/swp_cond_test_impl.S
new file mode 100644
index 000000000000..d29db44832b1
--- /dev/null
+++ b/tests/sys/compat32/aarch64/swp_cond_test_impl.S
@@ -0,0 +1,413 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Warner Losh
+ * Copyright (c) 2023 Stormshield
+ * Copyright (c) 2023 Klara, Inc.
+ */
+
+#include <sys/syscall.h>
+
+#define	STDOUT_FILENO	1
+#define	SWP_MAGIC	0xffc0
+#define	SWPB_MAGIC	0xc0c0
+
+	.text
+	.file "swp_test.S"
+	.syntax unified
+	.globl main
+	.p2align 2
+	.type main,%function
+	.code 32
+
+main:
+	sub sp, #0x04
+	/* r4 is our failed test counter */
+	mov r4, #0
+	/* r6 is our current teset counter */
+	mov r6, #1
+
+	movw r0, :lower16:.L.testheader
+	movt r0, :upper16:.L.testheader
+	ldr r1, =(.L.testheaderEnd - .L.testheader - 1)
+	bl print
+
+	/* eq */
+	bl reset
+	mov r1, #SWP_MAGIC
+	cmp r1, r1
+	swpeq r0, r1, [r0]
+	bl expect_success
+
+	/* Returned 0 (bad) or 1 (ok) */
+	cmp r0, #0
+	beq 1f
+
+	/* !eq */
+	bl reset
+	mov r1, #SWP_MAGIC
+	mov r2, #0
+	cmp r1, r2
+	swpeq r0, r1, [r0]
+	bl expect_fail
+
+	/* Don't care about the return of the second one, just print */
+1:
+	movw r0, :lower16:.L.eq
+	movt r0, :upper16:.L.eq
+	ldr r1, =(.L.eqEnd - .L.eq - 1)
+	bl print_result
+	add r6, r6, #1	/* Next test */
+
+	/* cs */
+	bl reset
+	mov r1, #SWP_MAGIC
+	movw r3, #0xffff
+	movt r3, #0xffff
+	/* Overflow */
+	adds r2, r3, r3
+	swpcs r0, r1, [r0]
+	bl expect_success
+
+	/* Returned 0 (bad) or 1 (ok) */
+	cmp r0, #0
+	beq 2f
+
+	/* !cs */
+	bl reset
+	mov r1, #SWP_MAGIC
+	mov r3, #0x00
+	adds r2, r3, #0x08
+	swpcs r0, r1, [r0]
+	bl expect_fail
+
+	/* Don't care about the return of the second one, just print */
+2:
+	movw r0, :lower16:.L.cs
+	movt r0, :upper16:.L.cs
+	ldr r1, =(.L.csEnd - .L.cs - 1)
+	bl print_result
+	add r6, r6, #1	/* Next test */
+
+	/* mi */
+	bl reset
+	mov r1, #SWP_MAGIC
+	mov r2, #0
+	/* Underflow */
+	subs r2, r2, #0x05
+	swpmi r0, r1, [r0]
+	bl expect_success
+
+	/* Returned 0 (bad) or 1 (ok) */
+	cmp r0, #0
+	beq 3f
+
+	/* !mi */
+	bl reset
+	mov r1, #SWP_MAGIC
+	mov r2, #0x10
+	subs r2, r2, #0x08
+	swpmi r0, r1, [r0]
+	bl expect_fail
+
+	/* Don't care about the return of the second one, just print */
+3:
+	movw r0, :lower16:.L.mi
+	movt r0, :upper16:.L.mi
+	ldr r1, =(.L.miEnd - .L.mi - 1)
+	bl print_result
+	add r6, r6, #1	/* Next test */
+
+	/* vs */
+	bl reset
+	mov r1, #SWP_MAGIC
+	movw r3, #0xffff
+	movt r3, #0x7fff
+	/* Overflow */
+	adds r2, r3, #0x10
+	swpvs r0, r1, [r0]
+	bl expect_success
+
+	/* Returned 0 (bad) or 1 (ok) */
+	cmp r0, #0
+	beq 4f
+
+	/* !vs */
+	bl reset
+	mov r1, #SWP_MAGIC
+	mov r3, #0x00
+	adds r2, r3, #0x08
+	swpvs r0, r1, [r0]
+	bl expect_fail
+
+	/* Don't care about the return of the second one, just print */
+4:
+	movw r0, :lower16:.L.vs
+	movt r0, :upper16:.L.vs
+	ldr r1, =(.L.vsEnd - .L.vs - 1)
+	bl print_result
+	add r6, r6, #1	/* Next test */
+
+	/* hi */
+	bl reset
+	mov r1, #SWP_MAGIC
+	mov r2, #0x00
+	mov r3, #0x01
+	cmp r3, r2
+	swphi r0, r1, [r0]
+	bl expect_success
+
+	/* Returned 0 (bad) or 1 (ok) */
+	cmp r0, #0
+	beq 5f
+
+	/* !hi */
+	bl reset
+	mov r1, #SWP_MAGIC
+	mov r2, #0x00
+	mov r3, #0x01
+	cmp r2, r3
+	swphi r0, r1, [r0]
+	bl expect_fail
+
+	/* Don't care about the return of the second one, just print */
+5:
+	movw r0, :lower16:.L.hi
+	movt r0, :upper16:.L.hi
+	ldr r1, =(.L.hiEnd - .L.hi - 1)
+	bl print_result
+	add r6, r6, #1	/* Next test */
+
+	/* ge */
+	bl reset
+	mov r1, #SWP_MAGIC
+	mov r2, #0x01
+	cmp r2, r2
+	swpge r0, r1, [r0]
+	bl expect_success
+
+	/* Returned 0 (bad) or 1 (ok) */
+	cmp r0, #0
+	beq 6f
+
+	/* !ge */
+	bl reset
+	mov r1, #SWP_MAGIC
+	mov r2, #0x00
+	mov r3, #0x01
+	cmp r2, r3
+	swpge r0, r1, [r0]
+	bl expect_fail
+
+	/* Don't care about the return of the second one, just print */
+6:
+	movw r0, :lower16:.L.ge
+	movt r0, :upper16:.L.ge
+	ldr r1, =(.L.geEnd - .L.ge - 1)
+	bl print_result
+	add r6, r6, #1	/* Next test */
+
+	/* gt */
+	bl reset
+	mov r1, #SWP_MAGIC
+	mov r2, #0x00
+	mov r3, #0x01
+	cmp r3, r2
+	swpgt r0, r1, [r0]
+	bl expect_success
+
+	/* Returned 0 (bad) or 1 (ok) */
+	cmp r0, #0
+	beq 7f
+
+	/* !ge */
+	bl reset
+	mov r1, #SWP_MAGIC
+	mov r2, #0x00
+	mov r3, #0x01
+	cmp r2, r3
+	swpgt r0, r1, [r0]
+	bl expect_fail
+
+	/* Don't care about the return of the second one, just print */
+7:
+	movw r0, :lower16:.L.gt
+	movt r0, :upper16:.L.gt
+	ldr r1, =(.L.gtEnd - .L.gt - 1)
+	bl print_result
+	add r6, r6, #1	/* Next test */
+
+	mov r0, r4	/* retval */
+	ldr r7, =SYS_exit
+	swi 0
+
+	.p2align 2
+	.type print_result,%function
+	.code 32
+print_result:
+	push {r4, r5, lr}
+	/* Save the label, size for our result */
+	mov r4, r0
+	mov r5, r1
+
+	movw r0, :lower16:.L.ok
+	movt r0, :upper16:.L.ok
+	ldr r1, =(.L.okEnd - .L.ok - 1)
+	bl print
+	mov r0, r6
+	add r0, #0x30 /* "0" + test number */
+	mov r1, #0x01
+	str r0, [sp]
+	mov r0, sp
+	bl print
+	movw r0, :lower16:.L.swp
+	movt r0, :upper16:.L.swp
+	ldr r1, =(.L.swpEnd - .L.swp - 1)
+	bl print
+	mov r0, r4
+	mov r1, r5
+	bl print
+	movw r0, :lower16:.L.term
+	movt r0, :upper16:.L.term
+	ldr r1, =(.L.termEnd - .L.term - 1)
+	bl print
+
+	pop {r4, r5, lr}
+	bx lr
+
+	.p2align 2
+	.type reset,%function
+	.code 32
+reset:
+	/* Reset sp[0] and return the address used */
+	mov r0, #0x03
+	str r0, [sp]
+	mov r0, sp
+	bx lr
+
+	.p2align 2
+	.type expect_fail,%function
+	.code 32
+expect_fail:
+	/* Just check the stack value */
+	ldr r0, [sp]
+	mov r1, #0x03
+	cmp r0,  r1
+	bne 1f
+
+	/* Success (not swapped) */
+	mov r0, #1
+	bx lr
+
+1:
+	/* Fail (swapped) */
+	/* Print the "not" part */
+	movw r0, :lower16:.L.not
+	movt r0, :upper16:.L.not
+	ldr r1, =(.L.notEnd - .L.not - 1)
+	push {lr}
+	bl print
+	pop {lr}
+
+	/* Failed */
+	add r4, r4, #1
+	mov r0, #0
+	bx lr
+
+	.p2align 2
+	.type expect_success,%function
+	.code 32
+expect_success:
+	/* Old value should be 3 */
+	cmp r0, #0x03
+	beq 1f
+	b 3f
+
+1:
+	/* Check stack value */
+	ldr r0, [sp]
+	mov r1, #SWP_MAGIC
+	cmp r0, r1
+	beq 2f
+	b 3f
+
+2:
+	mov r0, #1
+	bx lr
+
+3:
+	/* Print the "not" part */
+	movw r0, :lower16:.L.not
+	movt r0, :upper16:.L.not
+	ldr r1, =(.L.notEnd - .L.not - 1)
+	push {lr}
+	bl print
+	pop {lr}
+
+	/* Failed */
+	add r4, r4, #1
+	mov r0, #0
+	bx lr
+
+	.p2align 2
+	.type print,%function
+	.code 32
+print:
+	/* r0 - string, r1 = size */
+	mov r2, r1
+	mov r1, r0
+	ldr r0, =STDOUT_FILENO
+	ldr r7, =SYS_write
+	swi 0
+
+	bx lr
+
+.L.testheader:
+	.asciz "1..7\n"
+.L.testheaderEnd:
+	.size .L.testheader, .L.testheaderEnd - .L.testheader
+
+.L.not:
+	.asciz "not "
+.L.notEnd:
+	.size .L.not, .L.notEnd - .L.not
+.L.ok:
+	.asciz "ok "
+.L.okEnd:
+	.size .L.ok, .L.okEnd - .L.ok
+.L.swp:
+	.asciz " - swp"
+.L.swpEnd:
+	.size .L.swp, .L.swpEnd - .L.swp
+.L.eq:
+	.asciz "eq"
+.L.eqEnd:
+	.size .L.eq, .L.eqEnd - .L.eq
+.L.cs:
+	.asciz "cs"
+.L.csEnd:
+	.size .L.cs, .L.csEnd - .L.cs
+.L.mi:
+	.asciz "mi"
+.L.miEnd:
+	.size .L.mi, .L.miEnd - .L.mi
+.L.vs:
+	.asciz "vs"
+.L.vsEnd:
+	.size .L.vs, .L.vsEnd - .L.vs
+.L.hi:
+	.asciz "hi"
+.L.hiEnd:
+	.size .L.hi, .L.hiEnd - .L.hi
+.L.ge:
+	.asciz "ge"
+.L.geEnd:
+	.size .L.ge, .L.geEnd - .L.ge
+.L.gt:
+	.asciz "gt"
+.L.gtEnd:
+	.size .L.gt, .L.gtEnd - .L.gt
+.L.term:
+	.asciz "\n"
+.L.termEnd:
+	.size .L.term, .L.termEnd - .L.term
diff --git a/tests/sys/compat32/aarch64/swp_test.sh b/tests/sys/compat32/aarch64/swp_test.sh
new file mode 100644
index 000000000000..8edfa43b3e7e
--- /dev/null
+++ b/tests/sys/compat32/aarch64/swp_test.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+scriptdir=$(dirname $(realpath "$0"))
+
+. ${scriptdir}/common.sh
+
+# Ensure emul_swp is enabled just for this test; we'll turn it back off if
+# it wasn't enabled before the test.
+emul_swpval=$(sysctl -n compat.arm.emul_swp)
+sysctl compat.arm.emul_swp=1 >/dev/null
+${scriptdir}/swp_test_impl
+if [ "$emul_swpval" -ne 1 ]; then
+	sysctl compat.arm.emul_swp="$emul_swpval" >/dev/null
+fi
diff --git a/tests/sys/compat32/aarch64/swp_test_impl.S b/tests/sys/compat32/aarch64/swp_test_impl.S
new file mode 100644
index 000000000000..0e8047f1a6cf
--- /dev/null
+++ b/tests/sys/compat32/aarch64/swp_test_impl.S
@@ -0,0 +1,216 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Warner Losh
+ * Copyright (c) 2023 Stormshield
+ * Copyright (c) 2023 Klara, Inc.
+ */
+
+#include <sys/syscall.h>
+
+#define	STDOUT_FILENO	1
+#define	SWP_MAGIC	0xffc0
+#define	SWPB_MAGIC	0xc0c0
+
+	.text
+	.file "swp_test.S"
+	.syntax unified
+	.globl main
+	.p2align 2
+	.type main,%function
+	.code 32
+
+main:
+	sub sp, #0x04
+	/* r4 is our failed test counter */
+	mov r4, #0
+
+	movw r0, :lower16:.L.testheader
+	movt r0, :upper16:.L.testheader
+	ldr r1, =(.L.testheaderEnd - .L.testheader - 1)
+	bl print
+
+	/* Target address */
+	mov r0, #0x03
+	str r0, [sp]
+	mov r0, sp
+
+	/* Load value */
+	mov r1, #SWP_MAGIC
+
+	/* swp it */
+	swp r0, r1, [r0]
+
+	/* Old value should be 3 */
+	cmp r0, #0x03
+	bne 1f
+
+	/* Check stack value */
+	ldr r0, [sp]
+	mov r1, #SWP_MAGIC
+	cmp r0, r1
+	bne 1f
+	b 2f
+
+1:
+	/* Denote the failed test */
+	add r4, #1
+	/* "No" part of the notification */
+	movw r0, :lower16:.L.boknot
+	movt r0, :upper16:.L.boknot
+	ldr r1, =(.L.boknotEnd - .L.boknot - 1)
+	bl print
+
+2:
+	/* Notify */
+	movw r0, :lower16:.L.ok1
+	movt r0, :upper16:.L.ok1
+	ldr r1, =(.L.ok1End - .L.ok1 - 1)
+	bl print
+
+	movw r5, #SWPB_MAGIC
+	movt r5, #SWPB_MAGIC
+
+	/* Using r6 as our accumulator */
+	mov r6, sp
+	/* Simplify the loop */
+	sub r6, #1
+3:
+	/* Restore our magic value every time */
+	str r5, [sp]
+	/* Move on to the next byte */
+	add r6, #1
+
+	/* swp it in */
+	mov r0, r6
+	mov r1, #3
+	swpb r0, r1, [r0]
+
+	/* Check the old value */
+	cmp r0, #0xc0
+	bne 6f
+
+	/* Check the stack value */
+	ldrb r0, [r6]
+	cmp r0, #0x03
+	bne 6f
+
+	/* Just loop over the rest of the word and check those values. */
+	mov r1, r6
+	sub r1, sp
+
+	mov r0, #0x00
+4:
+	cmp r0, r1
+	beq 5f
+
+	/* Check the next byte */
+	ldrb r3, [sp, r0]
+	cmp r3, #0xc0
+	bne 6f
+
+5:
+	add r0, #0x01
+	cmp r0, #0x04
+	/* Hit the end, this one succeeded */
+	beq 7f
+
+	/* Move on to the next byte */
+	b 4b
+
+6:
+	/* Denote the failed test */
+	add r4, #1
+	/* "No" part of the notification */
+	movw r0, :lower16:.L.boknot
+	movt r0, :upper16:.L.boknot
+	ldr r1, =(.L.boknotEnd - .L.boknot - 1)
+	bl print
+
+	/* FALLTHROUGH */
+7:
+	/* "ok" part */
+	movw r0, :lower16:.L.bok
+	movt r0, :upper16:.L.bok
+	ldr r1, =(.L.bokEnd - .L.bok - 1)
+	bl print
+
+	/* test number */
+	mov r0, r6
+	sub r0, sp
+	add r0, #0x32 /* "0" + 2 because we start at test 2. */
+	mov r1, #0x01
+	str r0, [sp]
+	mov r0, sp
+	bl print
+
+	/* boklabel */
+	movw r0, :lower16:.L.boklabel
+	movt r0, :upper16:.L.boklabel
+	ldr r1, =(.L.boklabelEnd - .L.boklabel - 1)
+	bl print
+
+	/* index */
+	mov r0, r6
+	sub r0, sp
+	add r0, #0x30 /* "0" */
+	str r0, [sp]
+	mov r0, sp
+	mov r1, #0x01
+	bl print
+
+	/* bokterm */
+	movw r0, :lower16:.L.bokterm
+	movt r0, :upper16:.L.bokterm
+	ldr r1, =(.L.boktermEnd - .L.bokterm - 1)
+	bl print
+
+	mov r0, sp
+	add r0, #0x3
+	cmp r0, r6
+	bne 3b
+
+	mov r0, r4	/* retval */
+	ldr r7, =SYS_exit
+	swi 0
+
+	.p2align 2
+	.type print,%function
+	.code 32
+print:
+	/* r0 - string, r1 = size */
+	mov r2, r1
+	mov r1, r0
+	ldr r0, =STDOUT_FILENO
+	ldr r7, =SYS_write
+	swi 0
+
+	bx lr
+
+.L.testheader:
+	.asciz "1..5\n"
+.L.testheaderEnd:
+	.size .L.testheader, .L.testheaderEnd - .L.testheader
+
+	/* Maybe not the most efficient, but meh. */
+.L.ok1:
+	.asciz "ok 1 - swp\n"
+.L.ok1End:
+	.size .L.ok1, .L.ok1End - .L.ok1
+
+.L.boknot:
+	.asciz "not "
+.L.boknotEnd:
+	.size .L.boknot, .L.boknotEnd - .L.boknot
+.L.bok:
+	.asciz "ok "
+.L.bokEnd:
+	.size .L.bok, .L.bokEnd - .L.bok
+.L.boklabel:
+	.asciz " - swpb["
+.L.boklabelEnd:
+	.size .L.boklabel, .L.boklabelEnd - .L.boklabel
+.L.bokterm:
+	.asciz "]\n"
+.L.boktermEnd:
+	.size .L.bokterm, .L.boktermEnd - .L.bokterm
diff --git a/tools/regression/compat32/aarch64/Makefile b/tools/regression/compat32/aarch64/Makefile
new file mode 100644
index 000000000000..34428a58caa1
--- /dev/null
+++ b/tools/regression/compat32/aarch64/Makefile
@@ -0,0 +1,4 @@
+ACFLAGS= -target armv7-unknown-freebsd${OS_REVISION} -nostdlib -Wl,-e -Wl,main -static -mhwdiv=arm
+
+swp_test_impl: swp_test_impl.S
+	${CC} ${ACFLAGS} -o ${.TARGET} ${.ALLSRC}
diff --git a/tools/regression/compat32/aarch64/swp_test_impl.S b/tools/regression/compat32/aarch64/swp_test_impl.S
new file mode 100644
index 000000000000..9f28ab17748e
--- /dev/null
+++ b/tools/regression/compat32/aarch64/swp_test_impl.S
@@ -0,0 +1,410 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Warner Losh
+ * Copyright (c) 2023 Stormshield
+ * Copyright (c) 2023 Klara, Inc.
+ */
+
+#include <sys/syscall.h>
+
+#define	STDOUT_FILENO	1
+
+#define	MUTEX_LOCKED	0x01
+#define	MUTEX_UNLOCKED	0x00
+
+#define	STACK_SIZE	4096
+#define	TLS_SIZE	4096
+
+	.text
+	.file "swp_test.S"
+	.syntax unified
+	.globl main
+	.p2align 2
+	.type main,%function
+	.code 32
+
+main:
+	/*
+	 * Stack slots:
+	 * 0 - Sync word
+	 * 1 - Thread id
+	 * 2 - Shared word
+	 */
+	sub sp, sp, #12
+
+	/* Print a message */
+	movw r0, :lower16:.L.mainmsg
+	movt r0, :upper16:.L.mainmsg
+	ldr r1, =(.L.mainmsgEnd - .L.mainmsg - 1)
+	bl print
+
+	/* Create two secondary threads */
+	mov r0, #1
+	str r0, [sp, #4]	/* Thread ID */
+	movw r0, :lower16:secondary_thread
+	movt r0, :upper16:secondary_thread
+	mov r1, sp
+	movw r2, :lower16:stack1
+	movt r2, :upper16:stack1
+	movw r3, :lower16:tls1
+	movt r3, :upper16:tls1
+	bl create_thr
+
+1:
+	/*
+	 * Wait for the first new thread to ack its existence by
+	 * incrementing the thread id.
+	 */
+	ldr r0, [sp, #4]
+	cmp r0, #1
+	bne 2f
+	ldr r7, =SYS_sched_yield
+	swi 0
+	b 1b
+
+2:
+	/* Create thread #2 */
+	movw r0, :lower16:secondary_thread
+	movt r0, :upper16:secondary_thread
+	mov r1, sp
+	movw r2, :lower16:stack2
+	movt r2, :upper16:stack2
+	movw r3, :lower16:tls2
+	movt r3, :upper16:tls2
+	bl create_thr
+
+3:
+	/*
+	 * Wait for the first new thread to ack its existence by
+	 * incrementing the thread id.
+	 */
+	ldr r0, [sp, #4]
+	cmp r0, #2
+	bne 4f
+	ldr r7, =SYS_sched_yield
+	swi 0
+	b 3b
+
+	/* Loop */
+4:
+	mov r0, sp
+	mov r1, #0	/* Thread loop */
+	add r2, sp, #8
+	bl thread_loop
+	b 4b
+
+	/* UNREACHABLE */
+	mov r0, #0
+	ldr r7, =SYS_exit
+	swi 0
+
+	.p2align 2
+	.type secondary_thread,%function
+	.code 32
+secondary_thread:
+	/*
+	 * On entry, r0 is where we stashed our sync word and
+	 * ack word (thread ID).
+	 *
+	 * Stash the sync word in r4, thread ID in r5.
+	 */
+	mov r4, r0
+	ldr r5, [r0, #4]
+
+	/* Print a message */
+	movw r0, :lower16:.L.secondarymsg
+	movt r0, :upper16:.L.secondarymsg
+	ldr r1, =(.L.secondarymsgEnd - .L.secondarymsg - 1)
+	bl print
+
+	/* Acknowledge that we started */
+	add r0, r5, #1
+	str r0, [r4, #4]
+
+1:
+	mov r0, r4
+	mov r1, r5
+	add r2, r4, #8
+	bl thread_loop
+	b 1b
+
+	.p2align 2
+	.type thread_loop,%function
+	.code 32
+thread_loop:
+	push {r4, r5, r6, r7, r8, lr}
+
+	/*
+	 * r0 == sync word
+	 * r1 == thread ID
+	 * r2 == shared word
+	 */
+	mov r4, r0
+	mov r5, r1
+	mov r6, r2
+	bl lock_mutex_swp
+	str r5, [r6] /* Write the thread ID */
+	bl random_cycles
+
+	# Save off the now cycle count */
+	mov r8, r0
+
+	/* Print the thread ID and cycle count */
+	mov r0, r5
+	mov r1, #0
+	bl printnum
+
+	/* Separator */
+	movw r0, :lower16:.L.idsep
+	movt r0, :upper16:.L.idsep
+	ldr r1, =(.L.idsepEnd - .L.idsep - 1)
*** 249 LINES SKIPPED ***