[PATCH 1/3] Initial support for ARM64, incompatible with x86_64

Valerie Aurora <[email protected]> Tue, 24 Feb 2026 17:33:24 +0100
Newsgroups dev.linux.lists.rpdfs-devel
Message-ID <[email protected]>
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