* [PATCH 1/3] Initial support for ARM64, incompatible with x86_64
2026-02-24 16:33 [PATCH 0/3] rpdfs-progs: ARM64 support and documentation Valerie Aurora
@ 2026-02-24 16:33 ` Valerie Aurora
2026-02-25 23:36 ` Zach Brown
2026-02-24 16:33 ` [PATCH 2/3] Workaround for short read from io_uring on aarch64 Valerie Aurora
2026-02-24 16:33 ` [PATCH 3/3] Update documentation Valerie Aurora
2 siblings, 1 reply; 7+ messages in thread
From: Valerie Aurora @ 2026-02-24 16:33 UTC (permalink / raw)
To: rpdfs-devel; +Cc: Valerie Aurora
A quick hack to support task switching on ARM64 without any attempt to
make it portable or compatible with x86_64. Will come back to it later
if there is interest.
---
Makefile | 9 +++--
utask/utask.c | 17 +++++++++-
utask/utask_asm.S | 84 ++++++++++++++++++++++++++++++----------------
utask/utask_defs.h | 2 +-
4 files changed, 79 insertions(+), 33 deletions(-)
diff --git a/Makefile b/Makefile
index 8253885..6511202 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,9 @@
# make every target depend on the makefile
.EXTRA_PREREQS:= $(abspath $(lastword $(MAKEFILE_LIST)))
-CFLAGS := -I. -O2 -ggdb -Wall -Werror -D_FILE_OFFSET_BITS=64 -msse4.2 -fno-strict-aliasing -fno-omit-frame-pointer
+#CFLAGS := -I. -O2 -ggdb -Wall -Werror -D_FILE_OFFSET_BITS=64 -msse4.2 -fno-strict-aliasing -fno-omit-frame-pointer
+
+CFLAGS := -I. -I/usr/lib64/ -O2 -ggdb -Wall -Werror -D_FILE_OFFSET_BITS=64 -fno-strict-aliasing -fno-omit-frame-pointer
# function entrance/exit profiling for uftrace
CFLAGS += -pg
@@ -16,6 +18,9 @@ LDFLAGS := -Wl,--gc-sections -lxxhash -luring -luuid -lsystemd
# provide dynamic symbol tables for backtrace()
LDFLAGS += -rdynamic
+# aarch64 needs this to find libraries
+LDFLAGS += -L/usr/lib64/
+
# build all c files in source directories
DIR := cli devd shared shared/lk utask
SRC := $(foreach d,$(DIR),$(wildcard $(d)/*.c))
@@ -73,7 +78,7 @@ $(OBJ_S): %.o: %.S utask/utask_defs.h utask/utask_gen_defs.h
utask/utask.s: utask/utask.c
gcc -I. -S -o ./utask/utask.s ./utask/utask.c
-# extract defines from complied asm so we can include it from asm source
+# extract defines from compiled asm so we can include it from asm source
utask/utask_gen_defs.h: utask/utask.s
grep '#define.*\<UTASK_ASM' utask/utask.s > utask/utask_gen_defs.h
diff --git a/utask/utask.c b/utask/utask.c
index 7006f5c..3575b67 100644
--- a/utask/utask.c
+++ b/utask/utask.c
@@ -97,7 +97,7 @@ static struct utask *get_current_utask(void)
struct utask *tsk;
unsigned long sp;
- asm("movq %%rsp, %0 \n\t"
+ asm("mov %0, sp \n\t"
: "=rm" (sp) : : );
tsk = (void *)(round_up(sp, UTASK_STACK_SIZE) - sizeof(struct utask));
@@ -405,6 +405,7 @@ int utask_create_name_nowake(char *name, utask_fn_t fn, void *data, struct utask
struct utask *tsk = NULL;
unsigned long after;
void *stack;
+ void *fp;
int ret;
ret = posix_memalign(&stack, UTASK_STACK_SIZE, UTASK_STACK_SIZE);
@@ -439,15 +440,29 @@ int utask_create_name_nowake(char *name, utask_fn_t fn, void *data, struct utask
if (!IS_ALIGNED(after, 16))
tsk->sp -= 16 - (after & 15);
+ /* for alignment (I think?) */
+ push_stack(tsk, NULL);
+
/* final null address to terminate backtracing unwinders */
push_stack(tsk, NULL);
+
/* first switch_to jumps to _entry */
push_stack(tsk, utask_entry);
+ /* aarch64 wants the return address and the frame pointer on the stack */
+ fp = tsk->sp;
+ push_stack(tsk, fp);
+
/* initial zeroed saved registers */
tsk->sp -= UTASK_SAVED_BYTES;
memset(tsk->sp, 0, UTASK_SAVED_BYTES);
+ /* aarch64 - write the frame pointer into the second to last register */
+ *(void **)(tsk->sp) = fp;
+
+ /* aarch64 - write the return address into the last register (link register) */
+ *(void **)(tsk->sp + sizeof(void *)) = utask_entry;
+
ret = 0;
out:
if (ret < 0)
diff --git a/utask/utask_asm.S b/utask/utask_asm.S
index a693520..c69a090 100644
--- a/utask/utask_asm.S
+++ b/utask/utask_asm.S
@@ -14,46 +14,48 @@
# and epilogue. So we save and restore them as we switch to and from
# stacks.
#
-# ``Registers %rbp, %rbx and %r12 through %r15 “belong” to the calling
+# ``Registers %rbp, %rbx and %r12 through %r15 "belong" to the calling
# function and the called function is required to preserve their
# values.'' -- x86_64 ABI.
#
.macro push_callee_saved
- push %rbx
- push %r12
- push %r13
- push %r14
- push %r15
- push %rbp
+ stp x19, x20, [sp, #-16]!
+ stp x21, x22, [sp, #-16]!
+ stp x23, x24, [sp, #-16]!
+ stp x25, x26, [sp, #-16]!
+ stp x27, x28, [sp, #-16]!
+ stp x29, x30, [sp, #-16]!
.endm
.macro pop_callee_saved
- pop %rbp
- pop %r15
- pop %r14
- pop %r13
- pop %r12
- pop %rbx
+ ldp x29, x30, [sp], #16
+ ldp x27, x28, [sp], #16
+ ldp x25, x26, [sp], #16
+ ldp x23, x24, [sp], #16
+ ldp x21, x22, [sp], #16
+ ldp x19, x20, [sp], #16
.endm
#
-# set rbx to the address of the utask struct found at the base of the
+# set x9 to the address of the utask struct found at the base of the
# stack.
#
-.macro load_utask_rbx
- movq %rsp, %rbx
- andq $UTASK_STACK_BASE_MASK, %rbx
- addq $(UTASK_STACK_SIZE - UTASK_ASM_SIZEOF_UTASK), %rbx
+.macro load_utask_x9
+ mov x9, sp
+ mov x10, #UTASK_STACK_BASE_MASK
+ and x9, x9, x10
+ mov x10, #(UTASK_STACK_SIZE - UTASK_ASM_SIZEOF_UTASK)
+ add x9, x9, x10
.endm
#
-# int utask_switch_to(utask *utask /* rdi */);
+# int utask_switch_to(utask *utask /* x0 */);
#
# Resume execution of a utask that was previously stopped by switching
# from the scheduler's stack to the task's.
#
# Save the calling scheduler's stack pointer, restore the utask's stack
-# pointer, and return into the address that the utask pushed as they
+# pointer, and return into the address that the utask saved as they
# last called _switch_from.
#
# The ret here is returning to the caller of _switch_from, which is void,
@@ -63,8 +65,18 @@
.type utask_switch_to, @function
utask_switch_to:
push_callee_saved
- movq %rsp, UTASK_ASM_OFFSETOF_SCHED_SP(%rdi)
- movq UTASK_ASM_OFFSETOF_SP(%rdi), %rsp
+ // store scheduler/caller sp into utask struct at x0
+ mov x9, x0
+ add x9, x9, UTASK_ASM_OFFSETOF_SCHED_SP
+ mov x10, sp
+ str x10, [x9]
+
+ // load utask sp from utask struct into sp
+ mov x9, x0
+ add x9, x9, UTASK_ASM_OFFSETOF_SP
+ ldr x10, [x9]
+ mov sp, x10
+
pop_callee_saved
ret
@@ -83,11 +95,22 @@ utask_switch_to:
.type utask_switch_from, @function
utask_switch_from:
push_callee_saved
- load_utask_rbx
- movq %rsp, UTASK_ASM_OFFSETOF_SP(%rbx)
- movq UTASK_ASM_OFFSETOF_SCHED_SP(%rbx), %rsp
+ load_utask_x9 // load the calling utask's utask struct into x9
+
+ // store the utask sp into utask struct
+ mov x10, x9
+ add x10, x10, UTASK_ASM_OFFSETOF_SP // add the utask sp offset
+ mov x11, sp
+ str x11, [x10] // store sp in utask sp
+
+ // load the scheduler sp into the sp of the utask struct in x9
+ mov x10, x9
+ add x10, x10, UTASK_ASM_OFFSETOF_SCHED_SP // add the sched sp offset
+ ldr x11, [x10]
+ mov sp, x11
+
pop_callee_saved
- movq $UTASK_RET_SCHEDULED, %rax
+ mov x0, UTASK_RET_SCHEDULED
ret
#
@@ -102,8 +125,11 @@ utask_switch_from:
.globl utask_finish
.type utask_finish, @function
utask_finish:
- load_utask_rbx
- movq UTASK_ASM_OFFSETOF_SCHED_SP(%rbx), %rsp
+ // load scheduler sp out of utask struct into sp
+ load_utask_x9
+ add x9, x9, UTASK_ASM_OFFSETOF_SCHED_SP // add the sched sp offset
+ ldr x10, [x9]
+ mov sp, x10
pop_callee_saved
- mov $UTASK_RET_FINISHED, %rax
+ mov x0, UTASK_RET_FINISHED // return UTASK_RET_FINISHED
ret
diff --git a/utask/utask_defs.h b/utask/utask_defs.h
index 5f186c3..f5cd62d 100644
--- a/utask/utask_defs.h
+++ b/utask/utask_defs.h
@@ -11,7 +11,7 @@
#define UTASK_STACK_SIZE (8 * 1024)
#define UTASK_STACK_BASE_MASK (~(UTASK_STACK_SIZE - 1))
-#define UTASK_SAVED_BYTES (6 * 8)
+#define UTASK_SAVED_BYTES (12 * 8)
#define UTASK_RET_SCHEDULED 0
#define UTASK_RET_FINISHED 1
--
2.49.0
^ permalink raw reply related [flat|nested] 7+ messages in thread