diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist --- 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 --- a/tests/sys/Makefile +++ b/tests/sys/Makefile @@ -10,6 +10,7 @@ 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 --- /dev/null +++ b/tests/sys/compat32/Makefile @@ -0,0 +1,6 @@ + +.if exists(${.CURDIR}/${MACHINE_ARCH}) +SUBDIR+= ${MACHINE_ARCH} +.endif + +.include diff --git a/tests/sys/compat32/Makefile.inc b/tests/sys/compat32/Makefile.inc new file mode 100644 --- /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 --- /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 diff --git a/tests/sys/compat32/aarch64/common.sh b/tests/sys/compat32/aarch64/common.sh new file mode 100644 --- /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 '\'; 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 --- /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 --- /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 + +#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 --- /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 --- /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 + +#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 --- /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 --- /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 + +#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) + bl print + + /* Cycle count */ + mov r0, r8 + mov r1, #1 + bl printnum + +1: + ldr r0, [r6] + cmp r0, r5 /* Check against the thread ID */ + bne 2f + str r5, [r6] + + /* + * Check if the count hit 0, otherwise go again. + */ + cmp r8, #0 + beq 3f + sub r8, r8, #1 + b 1b + +2: + /* exit(1) */ + mov r0, #1 + ldr r7, =SYS_exit + swi 0 + +3: + mov r0, r4 + bl unlock_mutex_swp + + /* + * Yield to lower the chance that we end up re-acquiring, the other two + * threads are still actively trying to acquire the lock. + */ + ldr r7, =SYS_sched_yield + swi 0 + + pop {r4, r5, r6, r7, r8, lr} + bx lr + + .p2align 2 + .type random_cycles,%function + .code 32 +random_cycles: + /* Return a random number < 4k */ + sub sp, sp, #4 + mov r0, sp + mov r1, #4 + mov r2, #0 + ldr r7, =SYS_getrandom + swi 0 + + /* + * Just truncate the result of getrandom(2) + * to put us within range. Naive, but functional. + */ + ldr r0, [sp] + mov r1, #0xfff + and r0, r0, r1 + add sp, sp, #4 + bx lr + + /* + * lock_mutex_swp and unlock_mutex_swp lifted from + * ARM documentation on SWP/SWPB. + */ + .p2align 2 + .type lock_mutex_swp,%function + .code 32 +lock_mutex_swp: + mov r2, #MUTEX_LOCKED + swp r1, r2, [r0] /* Swap in lock value. */ + cmp r1, r2 /* Check if we were locked already. */ + beq lock_mutex_swp /* Retry if so */ + bx lr /* Return locked */ + + .p2align 2 + .type unlock_mutex_swp,%function + .code 32 +unlock_mutex_swp: + mov r1, #MUTEX_UNLOCKED + str r1, [r0] /* Move in unlocked */ + bx lr + + .p2align 2 + .type create_thr,%function + .code 32 +create_thr: + /* + * r0 == start_func + * r1 == arg + * r2 == stack_base + * r3 == tls_base + */ + sub sp, sp, #56 + str r0, [sp, #4] /* start_func */ + str r1, [sp, #8] /* arg */ + str r2, [sp, #12] /* stack_base */ + mov r0, #STACK_SIZE + str r0, [sp, #16] /* stack_size */ + str r3, [sp, #20] /* tls_base */ + mov r0, #TLS_SIZE + str r0, [sp, #24] /* tls_size */ + mov r0, #0 + str r0, [sp, #28] + str r0, [sp, #32] + str r0, [sp, #36] + str r0, [sp, #40] + + add r0, sp, #4 /* &thrp */ + mov r1, #52 /* sizeof(thrp) */ + ldr r7, =SYS_thr_new + swi 0 + + add sp, sp, #56 + bx lr + + .p2align 2 + .type printnum,%function + .code 32 +printnum: + push {r4, r5, r6, r7, r8, r10, lr} + sub sp, #4 + + /* 1000000000 */ + movw r6, #0xca00 + movt r6, #0x3b9a + + udiv r5, r0, r6 + cmp r5, #9 + bhi abort + + /* r4 is our accumulator */ + mov r4, r0 + /* r5 to be used as our "significant bit" */ + mov r5, #0 + /* r10 is "output_newline" */ + mov r10, r1 + +1: + cmp r6, #0 + beq 4f + + /* Divide by current place */ + udiv r0, r4, r6 + /* Significant already? print anyways */ + cmp r5, #0 + bne 2f + + /* + * Not significant, maybe print. If we made it all the way to 1, we + * need to just print the 0 anyways. + */ + cmp r6, #1 + beq 2f + + cmp r0, #0 + bne 2f + b 3f /* Proceed */ + + /* Print */ +2: + mov r5, #1 + mov r8, r0 + add r0, r0, #0x30 + str r0, [sp] + mov r0, sp + mov r1, #1 + bl print + + /* Multiply back into place and subtract from accumulator */ + mul r0, r8, r6 + sub r4, r4, r0 + +3: + mov r3, #10 + udiv r6, r6, r3 + b 1b + +4: + cmp r10, #0 + beq 5f + + /* newline */ + mov r0, #0x0a + str r0, [sp] + mov r0, sp + mov r1, #1 + bl print + +5: + add sp, sp, #4 + pop {r4, r5, r6, r7, r8, r10, lr} + bx lr + +abort: + movw r0, :lower16:.L.badnum + movt r0, :upper16:.L.badnum + ldr r1, =(.L.badnumEnd - .L.badnum - 1) + bl print + + mov r0, #1 + 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.mainmsg: + .asciz "Main thread\n" +.L.mainmsgEnd: + .size .L.mainmsg, .L.mainmsgEnd - .L.mainmsg +.L.secondarymsg: + .asciz "Secondary thread\n" +.L.secondarymsgEnd: + .size .L.secondarymsg, .L.secondarymsgEnd - .L.secondarymsg +.L.badnum: + .asciz "Bad number\n" +.L.badnumEnd: + .size .L.badnum, .L.badnumEnd - .L.badnum +.L.idsep: + .asciz " - cycles " +.L.idsepEnd: + .size .L.idsep, .L.idsepEnd - .L.idsep + + .type stack1,%object + .local stack1 + .comm stack1,STACK_SIZE,1 + .type tls1,%object + .local tls1 + .comm tls1,TLS_SIZE,1 + + .type stack2,%object + .local stack2 + .comm stack2,STACK_SIZE,1 + .type tls2,%object + .local tls2 + .comm tls2,TLS_SIZE,1