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
This commit is contained in:
Kyle Evans 2023-05-15 10:42:16 -05:00
parent 4b500174dd
commit ccb59683b9
12 changed files with 1117 additions and 0 deletions

View File

@ -750,6 +750,8 @@
..
..
..
compat32
..
devrandom
..
dtrace

View File

@ -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

View File

@ -0,0 +1,6 @@
.if exists(${.CURDIR}/${MACHINE_ARCH})
SUBDIR+= ${MACHINE_ARCH}
.endif
.include <bsd.subdir.mk>

View File

@ -0,0 +1,4 @@
TESTSDIR= ${TESTSBASE}/sys/compat32
.include "../Makefile.inc"

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}

View File

@ -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)
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