From 7babf5d7b980978de3e3bfda0f3417d59a621a24 Mon Sep 17 00:00:00 2001 From: Clement Dieperink Date: Thu, 18 Dec 2025 17:40:54 +0100 Subject: [PATCH 1/2] fix bug on arm32 after musl support --- so3/arch/arm32/Makefile | 1 + so3/arch/arm32/exception.S | 38 +++++++++++++---------------- so3/arch/arm32/syscalls.c | 49 ++++++++++++++++++++++++++++++++++++++ so3/arch/arm32/thread.c | 9 ++++++- so3/arch/arm64/exception.S | 11 +++------ so3/arch/arm64/thread.c | 3 +++ so3/devices/serial.c | 30 ----------------------- so3/fs/elf.c | 14 +++++++++-- so3/kernel/process.c | 4 ++-- 9 files changed, 94 insertions(+), 65 deletions(-) create mode 100644 so3/arch/arm32/syscalls.c diff --git a/so3/arch/arm32/Makefile b/so3/arch/arm32/Makefile index a8d2bdbfe..1b03fb8e0 100644 --- a/so3/arch/arm32/Makefile +++ b/so3/arch/arm32/Makefile @@ -6,6 +6,7 @@ obj-y += thread.o obj-y += vfp.o obj-y += backtrace.o backtrace_asm.o obj-y += smccc-call.o +obj-y += syscalls.o obj-y += lib/ diff --git a/so3/arch/arm32/exception.S b/so3/arch/arm32/exception.S index 37feef58a..84bffe474 100644 --- a/so3/arch/arm32/exception.S +++ b/so3/arch/arm32/exception.S @@ -39,7 +39,7 @@ .extern current_thread .extern irq_handle -.extern syscall_handle +.extern arch_syscall_handle .extern dumpstack @@ -118,8 +118,8 @@ __prepare_sig_handler: bne __stack_alignment_fault @ Copy TLS to the new stack frame - ldr r0, [sp, #OFFSET_TLS_USR] - str r0, [sp, #(-SVC_STACK_FRAME_SIZE + OFFSET_TLS_USR)] + ldr r1, [sp, #OFFSET_TLS_USR] + str r1, [sp, #(-SVC_STACK_FRAME_SIZE + OFFSET_TLS_USR)] str sp, [sp, #(-SVC_STACK_FRAME_SIZE + OFFSET_SP)] @ save sp @@ -203,8 +203,8 @@ syscall_interrupt: stmia lr, {sp, lr}^ @ Save user space TLS context - mrc p15, 0, r0, c13, c0, 0 - str r0, [sp, #OFFSET_TLS_USR] + mrc p15, 0, lr, c13, c0, 3 + str lr, [sp, #OFFSET_TLS_USR] cmp r7, #SYSCALL_sigreturn beq __after_push_sp_usr @@ -223,7 +223,7 @@ __after_push_sp_usr: mov r0, sp cpsie i @ Re-enable interrupts - bl syscall_handle + bl arch_syscall_handle cpsid i @ Re-disable interrupts to be safe in regs manipulation @ Check if sigreturn has been called. In this case, we @@ -235,12 +235,14 @@ __after_push_sp_usr: add sp, sp, #SVC_STACK_FRAME_SIZE __no_sigreturn: -__ret_from_fork: @ Store the return value on the stack frame cmp r7, #SYSCALL_sigreturn strne r0, [sp, #OFFSET_R0] + // Entry point for new user threads +ret_from_fork: + #ifdef CONFIG_IPC_SIGNAL @ Is there any pending signals for this process? check_pending_signal @@ -248,7 +250,7 @@ __ret_from_fork: @ Restore user space TLS context ldr lr, [sp, #OFFSET_TLS_USR] - mcr p15, 0, lr, c13, c0, 0 + mcr p15, 0, lr, c13, c0, 3 @ get the saved spsr and adjust the stack pointer ldr lr, [sp, #OFFSET_PSR] @@ -266,14 +268,6 @@ __ret_from_fork: ldmia sp, {sp, lr, pc}^ - - -@ Used at entry point of a fork'd process (setting the return value to 0) -ret_from_fork: - mov r0, #0 - - b __ret_from_fork - .align 5 prefetch_abort: @@ -360,8 +354,8 @@ irq: stmeqia lr, {sp, lr}^ @ Save user space TLS context - mrc p15, 0, r0, c13, c0, 0 - str r0, [sp, #OFFSET_TLS_USR] + mrceq p15, 0, lr, c13, c0, 3 + streq lr, [sp, #OFFSET_TLS_USR] @ Retrieve the lr_irq to set the pc out of this routine ldr lr, [r0, #4] @ retrieve lr_irq to set lr_svc @@ -388,10 +382,6 @@ irq: check_pending_signal #endif /* CONFIG_IPC_SIGNAL */ - @ Restore user space TLS context - ldr lr, [sp, #OFFSET_TLS_USR] - mcr p15, 0, lr, c13, c0, 0 - ldr lr, [sp, #OFFSET_PSR] @ get the saved spsr and adjust the stack pointer msr spsr, lr @@ -403,6 +393,10 @@ irq: addeq lr, sp, #OFFSET_SP_USR ldmeqia lr, {sp, lr}^ + @ Restore user space TLS context + ldreq lr, [sp, #OFFSET_TLS_USR] + mcreq p15, 0, lr, c13, c0, 3 + @ Restore registers ldmia sp, {r0-r12} diff --git a/so3/arch/arm32/syscalls.c b/so3/arch/arm32/syscalls.c new file mode 100644 index 000000000..4a687f982 --- /dev/null +++ b/so3/arch/arm32/syscalls.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2026 Dieperink Clément + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include + +/* ARM32 specific syscalls to manage TLS */ +#define ARM_NR_set_tls 0x0f0005 +#define ARM_NR_get_tls 0x0f0006 + +extern void __get_syscall_args_ext(long *syscall_no); + +/** + * Handle special ARM32 syscalls, or fallback to generic syscall handling. + */ +long arch_syscall_handle(syscall_args_t *syscall_args) +{ + long syscall_no; + cpu_regs_t *user_regs = (cpu_regs_t *) arch_get_kernel_stack_frame(current()); + + /* Get addtional args of the syscall according to the ARM & SO3 ABI */ + __get_syscall_args_ext(&syscall_no); + + switch (syscall_no) { + case ARM_NR_get_tls: + return user_regs->tls_usr; + case ARM_NR_set_tls: + user_regs->tls_usr = syscall_args->args[0]; + return 0; + default: + return syscall_handle(syscall_args); + } +} diff --git a/so3/arch/arm32/thread.c b/so3/arch/arm32/thread.c index ff26aa07f..dc92101a4 100644 --- a/so3/arch/arm32/thread.c +++ b/so3/arch/arm32/thread.c @@ -55,6 +55,12 @@ void arch_prepare_cpu_regs(tcb_t *tcb, clone_args_t *args) if (args->flags & CLONE_SETTLS) user_regs->tls_usr = args->tls; + + /* Set return value to 0 to indicate new thread */ + user_regs->r0 = 0; + + /* Set kernel stack address used to restore stack on eret */ + user_regs->sp = get_kernel_stack_top(tcb->stack_slotID); } tcb->cpu_regs.lr = (unsigned long) ret_from_fork; @@ -79,8 +85,9 @@ void arch_restart_user_thread(tcb_t *tcb, th_fn_t fn_entry, addr_t stack_top) */ *user_regs = (cpu_regs_t) { .pc = (u32) fn_entry, - .psr = PSR_USR_MODE, + .psr = PSR_USR_MODE | PSR_I_BIT, .sp_usr = stack_top, + .sp = get_kernel_stack_top(tcb->stack_slotID), }; } diff --git a/so3/arch/arm64/exception.S b/so3/arch/arm64/exception.S index 4dadb33a4..2ce96b576 100644 --- a/so3/arch/arm64/exception.S +++ b/so3/arch/arm64/exception.S @@ -567,13 +567,13 @@ el01_sync_handler: // Check if sigreturn has been called. In this case, we // clean the stack frame which has been used to manage the user handler. cmp x8, #SYSCALL_rt_sigreturn - bne __ret_from_fork + bne ret_from_fork // Reset the stack frame by removing the one issued from sigreturn add sp, sp, #S_FRAME_SIZE - -__ret_from_fork: + // Entry point for new user threads +ret_from_fork: #ifdef CONFIG_IPC_SIGNAL // Is there any pending signals for this process? @@ -605,11 +605,6 @@ el01_1_irq_handler: eret -// Used at entry point of a fork'd process (setting the return value to 0) -ret_from_fork: - str xzr, [sp, #OFFSET_X0] - b __ret_from_fork - #if !defined(CONFIG_AVZ) && defined(CONFIG_SOO) .align 5 diff --git a/so3/arch/arm64/thread.c b/so3/arch/arm64/thread.c index 0daf82571..9ad64bae3 100644 --- a/so3/arch/arm64/thread.c +++ b/so3/arch/arm64/thread.c @@ -54,6 +54,9 @@ void arch_prepare_cpu_regs(tcb_t *tcb, clone_args_t *args) if (args->flags & CLONE_SETTLS) user_regs->tls_usr = args->tls; + + /* Set return value to 0 to indicate new thread */ + user_regs->x0 = 0; } tcb->cpu_regs.lr = (unsigned long) ret_from_fork; diff --git a/so3/devices/serial.c b/so3/devices/serial.c index 69664fbc9..a4fe97097 100644 --- a/so3/devices/serial.c +++ b/so3/devices/serial.c @@ -100,38 +100,8 @@ int serial_write(char *str, int len) /* This function will query the size of the serial terminal*/ int serial_gwinsize(struct winsize *wsz) { - /* - * The following code strongly depends on a patched version of QEMU which reacts - * to the '\254' ASCII code. When the emulated UART interface get this code, qemu - * queries the host via ioctl(stdout, TIOCGWINSZ) to retrieve the window size. - */ - - /* We want to reserve the access to the uart read. */ - - /* Prevent an interrupt on the UART generated by - * QEMU to read the two bytes. So far, we do not - * manage an internal buffer to read many chars. - */ - -#if defined(CONFIG_VIRT32) && !defined(CONFIG_SOO) - - serial_ops.disable_irq(); - - if (serial_write(SERIAL_GWINSZ, 1) == 0) - return -1; - - wsz->ws_row = serial_ops.get_byte(true); - wsz->ws_col = serial_ops.get_byte(true); - - serial_ops.enable_irq(); - -#else - wsz->ws_row = WINSIZE_ROW_SIZE_DEFAULT; wsz->ws_col = WINSIZE_COL_SIZE_DEFAULT; - -#endif - return 0; } diff --git a/so3/fs/elf.c b/so3/fs/elf.c index c05309a6b..5899debd2 100644 --- a/so3/fs/elf.c +++ b/so3/fs/elf.c @@ -165,6 +165,8 @@ void elf_load_sections(elf_img_info_t *elf_img_info) void elf_load_segments(elf_img_info_t *elf_img_info) { size_t i; + size_t segment_start, segment_end; + size_t page_start, page_end; /* Segments */ #ifdef CONFIG_ARCH_ARM32 @@ -199,8 +201,16 @@ void elf_load_segments(elf_img_info_t *elf_img_info) sizeof(struct elf64_phdr)); #endif - if (elf_img_info->segments[i].p_type == PT_LOAD) - elf_img_info->segment_page_count += (elf_img_info->segments[i].p_memsz >> PAGE_SHIFT) + 1; + if (elf_img_info->segments[i].p_type == PT_LOAD) { + /* Don't use only p_memsz to get page count as p_vaddr can be unaligned and additionnal page will be needed. */ + segment_start = elf_img_info->segments[i].p_vaddr; + segment_end = segment_start + elf_img_info->segments[i].p_memsz; + + page_start = segment_start >> PAGE_SHIFT; + page_end = (segment_end + PAGE_SIZE) >> PAGE_SHIFT; + + elf_img_info->segment_page_count += page_end - page_start; + } } LOG_DEBUG("segments use %d virtual pages\n", elf_img_info->segment_page_count); diff --git a/so3/kernel/process.c b/so3/kernel/process.c index d3bee955a..4c9f0ac05 100644 --- a/so3/kernel/process.c +++ b/so3/kernel/process.c @@ -74,9 +74,9 @@ static uint32_t pid_current = 1; static pcb_t *root_process = NULL; /* root process */ /* only the following sections are supported */ -#define SUPPORTED_SECTION_COUNT 8 +#define SUPPORTED_SECTION_COUNT 10 static const char *supported_section_names[SUPPORTED_SECTION_COUNT] = { - ".init", ".text", ".rodata", ".data", ".sbss", ".bss", ".scommon", ".fini", + ".init", ".text", ".rodata", ".data", ".sbss", ".bss", ".scommon", ".fini", ".init_array", ".fini_array", }; /* From 06b4ff1f2ca065fb3bf0a079fd05047aab82fec9 Mon Sep 17 00:00:00 2001 From: Clement Dieperink Date: Tue, 6 Jan 2026 14:36:36 +0100 Subject: [PATCH 2/2] fix nanosleep and futex --- so3/include/device/timer.h | 7 +++ so3/include/timer.h | 2 +- so3/kernel/delay.c | 8 +-- so3/kernel/futex.c | 4 +- so3/scripts/syscall_gen.sh | 23 ++++---- so3/syscall.tbl | 111 +++++++++++++++++++------------------ usr/src/time.c | 3 +- 7 files changed, 81 insertions(+), 77 deletions(-) diff --git a/so3/include/device/timer.h b/so3/include/device/timer.h index cdca9f822..3c6e1c91c 100644 --- a/so3/include/device/timer.h +++ b/so3/include/device/timer.h @@ -49,6 +49,13 @@ struct timeval32 { time32_t tv_usec; /* microseconds */ }; +/* Time conversion units - Dynamic size (for nanosleep syscall). */ + +struct long_timespec { + long tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; + /* All timing information below must be express in nanoseconds. The underlying hardware is responsible * to perform the necessary alignment on 64 bits. */ diff --git a/so3/include/timer.h b/so3/include/timer.h index 8905046a0..e5139cea9 100644 --- a/so3/include/timer.h +++ b/so3/include/timer.h @@ -126,7 +126,7 @@ void clocksource_timer_reset(void); void clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec); -SYSCALL_DECLARE(nanosleep, const struct timespec *req, struct timespec *rem); +SYSCALL_DECLARE(nanosleep, const struct long_timespec *req, struct long_timespec *rem); SYSCALL_DECLARE(gettimeofday_time32, struct timeval32 *tv, void *tz); SYSCALL_DECLARE(gettimeofday, struct timeval *tv, void *tz); diff --git a/so3/kernel/delay.c b/so3/kernel/delay.c index 59099520c..d2e14795d 100644 --- a/so3/kernel/delay.c +++ b/so3/kernel/delay.c @@ -116,12 +116,8 @@ void sleep(u64 ns) __sleep(ns); } -SYSCALL_DEFINE2(nanosleep, const struct timespec *, req, struct timespec *, rem) +SYSCALL_DEFINE2(nanosleep, const struct long_timespec *, req, struct long_timespec *, rem) { - if (req->tv_nsec != 0) - __sleep(req->tv_nsec); - else if (req->tv_sec != 0) - __sleep(SECONDS(req->tv_sec)); - + __sleep(SECONDS(req->tv_sec) + req->tv_nsec); return 0; } diff --git a/so3/kernel/futex.c b/so3/kernel/futex.c index 214b7f7bc..723f79567 100644 --- a/so3/kernel/futex.c +++ b/so3/kernel/futex.c @@ -78,8 +78,10 @@ static int do_futex_wait(uint32_t *futex_w, uint32_t val, const struct timespec spin_lock(&pcb->futex_lock); - if (list_empty(&futex->f_element)) + if (list_empty(&futex->f_element)) { + list_del(&futex->list); free(futex); + } spin_unlock_irqrestore(&pcb->futex_lock, flags); diff --git a/so3/scripts/syscall_gen.sh b/so3/scripts/syscall_gen.sh index bfd557716..4cde22f55 100755 --- a/so3/scripts/syscall_gen.sh +++ b/so3/scripts/syscall_gen.sh @@ -22,11 +22,12 @@ # The input files are: # # - syscall.tbl: Declares all syscalls available on SO3 without taking into -# account the CPU arch. This also provides configuration -# requirement for a given syscall. -# Requirement can be set to EMPTY_IMPLEMENTATION, which will -# set a default function returning -ENOSYS for syscalls that -# are intentionally not implemented. +# account the CPU arch, With the function name that will be +# called. This also provides configuration requirement for a +# given syscall. +# The function can be set to "empty", which is a default +# function returning -ENOSYS for syscalls that are +# intentionally not implemented. # - syscall.h.in: Contains all syscall number which are necessary implemented # in SO3. This is a copy of the same file from musl library. # @@ -82,15 +83,11 @@ declare -Ag sys_cond=() file_header "__SYSCALL_TABLE_H__" > "$outfile_table" valid_syscalls="$(grep -E "^[^#]" "$infile_table")" -while read name requirement; do +while read name func requirement; do sys_cond["$name"]="$requirement" echo "#ifdef SYSCALL_$name" - if [ "$requirement" = "EMPTY_IMPLEMENTATION" ]; then - echo -e "\t[SYSCALL_$name] = &__sys_empty," - else - echo -e "\t[SYSCALL_$name] = &__sys_$name," - fi + echo -e "\t[SYSCALL_$name] = &__sys_$func," echo "#endif" done < <(echo "$valid_syscalls") >> "$outfile_table" @@ -107,13 +104,13 @@ grep -E "^#define " "$infile_number" | sed "s/^#define __NR_//" | { continue fi - if [ -n "${sys_cond[$name]}" -a "${sys_cond[$name]}" != "EMPTY_IMPLEMENTATION" ]; then + if [ -n "${sys_cond[$name]}" ]; then echo "#ifdef CONFIG_${sys_cond[$name]}" fi echo "#define SYSCALL_$name $number" - if [ -n "${sys_cond[$name]}" -a "${sys_cond[$name]}" != "EMPTY_IMPLEMENTATION" ]; then + if [ -n "${sys_cond[$name]}" ]; then echo "#endif" fi done diff --git a/so3/syscall.tbl b/so3/syscall.tbl index 1821bdb37..8a1147545 100644 --- a/so3/syscall.tbl +++ b/so3/syscall.tbl @@ -16,62 +16,63 @@ # This file declare all syscalls available on SO3, without taking into account # of CPU arch. See scripts/syscall_gen.sh for more information. # -# Syscall name Configuration requirement -read -readv -write -writev -open -openat -close -lseek -_llseek -ioctl -dup -dup2 -dup3 -getdents64 -stat64 -fstatat64 -newfstatat -mmap -mmap2 -nanosleep -futex -sched_yield -pipe IPC_PIPE -pipe2 IPC_PIPE -rt_sigaction IPC_SIGNAL -kill IPC_SIGNAL -sigreturn IPC_SIGNAL -rt_sigreturn IPC_SIGNAL -rt_sigprocmask IPC_SIGNAL -getpid MMU -execve MMU -fork MMU -clone MMU -set_tid_address MMU -exit MMU -exit_group MMU -wait4 MMU -gettimeofday MMU -gettimeofday_time32 MMU -clock_gettime MMU -clock_gettime32 MMU -socket NET -connect NET -bind NET -listen NET -accept NET -recv NET -recvfrom NET -send NET -sendto NET -setsockopt NET -brk PROC_ENV +# Syscall name (MUSL) Syscall function Configuration requirement +read read +readv readv +write write +writev writev +open open +openat openat +close close +lseek lseek +_llseek _llseek +ioctl ioctl +dup dup +dup2 dup2 +dup3 dup3 +getdents64 getdents64 +stat64 stat64 +fstatat64 fstatat64 +newfstatat newfstatat +mmap mmap +mmap2 mmap2 +nanosleep nanosleep +futex futex +sched_yield sched_yield +pipe pipe IPC_PIPE +pipe2 pipe2 IPC_PIPE +rt_sigaction rt_sigaction IPC_SIGNAL +kill kill IPC_SIGNAL +sigreturn sigreturn IPC_SIGNAL +rt_sigreturn rt_sigreturn IPC_SIGNAL +rt_sigprocmask rt_sigprocmask IPC_SIGNAL +getpid getpid MMU +execve execve MMU +fork fork MMU +clone clone MMU +set_tid_address set_tid_address MMU +exit exit MMU +exit_group exit_group MMU +wait4 wait4 MMU +gettimeofday gettimeofday MMU +gettimeofday_time32 gettimeofday_time32 MMU +clock_gettime clock_gettime MMU +clock_gettime64 clock_gettime MMU +clock_gettime32 clock_gettime32 MMU +socket socket NET +connect connect NET +bind bind NET +listen listen NET +accept accept NET +recv recv NET +recvfrom recvfrom NET +send send NET +sendto sendto NET +setsockopt setsockopt NET +brk brk PROC_ENV # TODO: implement. Heavily used in MUSL, so remove warning about it for now. -munmap EMPTY_IMPLEMENTATION +munmap empty # mprotect allows to set memory protection (read/write/execute), which isn't supported. -mprotect EMPTY_IMPLEMENTATION +mprotect empty diff --git a/usr/src/time.c b/usr/src/time.c index 47141b241..c8b9e4cf4 100644 --- a/usr/src/time.c +++ b/usr/src/time.c @@ -5,6 +5,7 @@ #include #include +#include int main(int argc, char *argv[]) { @@ -13,6 +14,6 @@ int main(int argc, char *argv[]) while (true) { gettimeofday(&tv, NULL); - printf("# time(s) : %lu time(us) : %lu\n", tv.tv_sec, tv.tv_usec); + printf("# time(s) : %" PRIu64 " time(us) : %" PRIu64 "\n", tv.tv_sec, tv.tv_usec); } }