diff --git a/so3/arch/arm32/include/asm/hwcap.h b/so3/arch/arm32/include/asm/hwcap.h new file mode 100644 index 000000000..cf63eb016 --- /dev/null +++ b/so3/arch/arm32/include/asm/hwcap.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2025 Clément Dieperink + * + * 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 + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ASM_ARM_HWCAP_H +#define ASM_ARM_HWCAP_H + +/* clang-format off */ + +/* + * Hardware capabilities flags - for AT_HWCAP + * Copied from Linux arch/arm/include/asm/procinfo.h + */ +#define HWCAP_SWP (1 << 0) +#define HWCAP_HALF (1 << 1) +#define HWCAP_THUMB (1 << 2) +#define HWCAP_26BIT (1 << 3) /* Play it safe */ +#define HWCAP_FAST_MULT (1 << 4) +#define HWCAP_FPA (1 << 5) +#define HWCAP_VFP (1 << 6) +#define HWCAP_EDSP (1 << 7) +#define HWCAP_JAVA (1 << 8) +#define HWCAP_IWMMXT (1 << 9) +#define HWCAP_CRUNCH (1 << 10) /* Obsolete */ +#define HWCAP_THUMBEE (1 << 11) +#define HWCAP_NEON (1 << 12) +#define HWCAP_VFPv3 (1 << 13) +#define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */ +#define HWCAP_TLS (1 << 15) +#define HWCAP_VFPv4 (1 << 16) +#define HWCAP_IDIVA (1 << 17) +#define HWCAP_IDIVT (1 << 18) +#define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */ +#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT) +#define HWCAP_LPAE (1 << 20) +#define HWCAP_EVTSTRM (1 << 21) +#define HWCAP_FPHP (1 << 22) +#define HWCAP_ASIMDHP (1 << 23) +#define HWCAP_ASIMDDP (1 << 24) +#define HWCAP_ASIMDFHM (1 << 25) +#define HWCAP_ASIMDBF16 (1 << 26) +#define HWCAP_I8MM (1 << 27) + +/* + * ARMv7 flags used by Linux, see arch/arm/mm/proc-v7.S, macro __v7_proc + */ +#define HWCAP_ELF (HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT | HWCAP_EDSP | HWCAP_TLS) + +/* clang-format on */ + +#endif /* ASM_ARM_HWCAP */ diff --git a/so3/arch/arm64/include/asm/hwcap.h b/so3/arch/arm64/include/asm/hwcap.h new file mode 100644 index 000000000..0364a207f --- /dev/null +++ b/so3/arch/arm64/include/asm/hwcap.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2025 Clément Dieperink + * + * 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, see . + */ + +#ifndef ASM_ARM_HWCAP_H +#define ASM_ARM_HWCAP_H + +/* + * This isn't used by MUSL on AArch64, so simply let it to 0 for now. + */ +#define HWCAP_ELF 0 + +#endif /* ASM_ARM_HWCAP */ diff --git a/so3/include/elf.h b/so3/include/elf.h index 767ca1155..688bbb678 100644 --- a/so3/include/elf.h +++ b/so3/include/elf.h @@ -36,6 +36,8 @@ struct elf_img_info { uint32_t segment_page_count; }; +typedef Elf32_Off elf_addr_t; + #else struct elf_img_info { @@ -47,6 +49,8 @@ struct elf_img_info { uint64_t segment_page_count; }; +typedef Elf64_Off elf_addr_t; + #endif typedef struct elf_img_info elf_img_info_t; diff --git a/so3/include/uapi/linux/auxvec.h b/so3/include/uapi/linux/auxvec.h new file mode 100644 index 000000000..e23fed76e --- /dev/null +++ b/so3/include/uapi/linux/auxvec.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2025 Clément Dieperink + * + * 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, see . + */ + +#ifndef UAPI_LINUX_AUXVEC_H +#define UAPI_LINUX_AUXVEC_H + +/* clang-format off */ + +/* Copied from linux/include/uapi/linux/auxvec.h */ + +/* Symbolic values for the entries in the auxiliary table + put on the initial stack */ +#define AT_NULL 0 /* end of vector */ +#define AT_IGNORE 1 /* entry should be ignored */ +#define AT_EXECFD 2 /* file descriptor of program */ +#define AT_PHDR 3 /* program headers for program */ +#define AT_PHENT 4 /* size of program header entry */ +#define AT_PHNUM 5 /* number of program headers */ +#define AT_PAGESZ 6 /* system page size */ +#define AT_BASE 7 /* base address of interpreter */ +#define AT_FLAGS 8 /* flags */ +#define AT_ENTRY 9 /* entry point of program */ +#define AT_NOTELF 10 /* program is not ELF */ +#define AT_UID 11 /* real uid */ +#define AT_EUID 12 /* effective uid */ +#define AT_GID 13 /* real gid */ +#define AT_EGID 14 /* effective gid */ +#define AT_PLATFORM 15 /* string identifying CPU for optimizations */ +#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ +#define AT_CLKTCK 17 /* frequency at which times() increments */ +/* AT_* values 18 through 22 are reserved */ +#define AT_SECURE 23 /* secure mode boolean */ +#define AT_BASE_PLATFORM 24 /* string identifying real platform, may + * differ from AT_PLATFORM. */ +#define AT_RANDOM 25 /* address of 16 random bytes */ +#define AT_HWCAP2 26 /* extension of AT_HWCAP */ +#define AT_RSEQ_FEATURE_SIZE 27 /* rseq supported feature size */ +#define AT_RSEQ_ALIGN 28 /* rseq allocation alignment */ +#define AT_HWCAP3 29 /* extension of AT_HWCAP */ +#define AT_HWCAP4 30 /* extension of AT_HWCAP */ + +#define AT_EXECFN 31 /* filename of program */ + +#ifndef AT_MINSIGSTKSZ +#define AT_MINSIGSTKSZ 51 /* minimal stack size for signal delivery */ +#endif + +/* Common in asm/auxvec.h of arm and arm64 */ +#define AT_SYSINFO_EHDR 33 + +/* clang-format on */ + +#endif /* _UAPI_LINUX_AUXVEC_H */ diff --git a/so3/kernel/process.c b/so3/kernel/process.c index 0caa26cf0..4cfac83bb 100644 --- a/so3/kernel/process.c +++ b/so3/kernel/process.c @@ -32,6 +32,9 @@ #include #include #include +#include + +#include #include @@ -39,10 +42,19 @@ #include #include #include +#include #ifndef CONFIG_ARCH_ARM32 #include #endif +/* Structure to temporary save exec args/env */ +typedef struct { + int argc; + int envc; + size_t strings_size; + char arg_env[PAGE_SIZE]; +} args_env_t; + static char *proc_state_strings[5] = { [PROC_STATE_NEW] = "NEW", [PROC_STATE_READY] = "READY", [PROC_STATE_RUNNING] = "RUNNING", [PROC_STATE_WAITING] = "WAITING", [PROC_STATE_ZOMBIE] = "ZOMBIE", @@ -390,150 +402,152 @@ static void release_proc_pages(pcb_t *pcb) */ addr_t preserve_args_and_env(int argc, char **argv, char **envp) { - char *args_p, *args_str_p; - void *args; - char **__args; + size_t str_len; + args_env_t *saved; int i; if ((argc > 0) && (argv == NULL)) { return -EINVAL; } - /* Page storing the args & env strings - keeps a reference to it. - */ - args = malloc(PAGE_SIZE); - BUG_ON(args == NULL); - - memset(args, 0, PAGE_SIZE); - - args_p = (char *) args; - if (!argc) - i = 1; - else - i = argc; - - memcpy(args_p, &i, sizeof(int)); - - /* Number of args */ - args_p += sizeof(int); - - /* Store the array of strings for args & env */ - - /* The array starts right after argc (stored on 4 bytes). - * We find the addresses of the var strings followed by the addresses of - * env strings. The var strings come after followed by the env strings. - */ - - /* Manage the addresses of strings; determine the start of the first arg - * string. */ - - if (!argc) /* At least one arg containing the process name */ - args_p += sizeof(char *); - else - args_p += sizeof(char *) * argc; - - /* Environment string addresses */ - if (!envp) { - *((addr_t *) args_p) = 0; /* Keep array-end with NULL */ - args_p += sizeof(char *); - - } else { - i = 0; - do { - args_p = args_p + sizeof(char *); - } while (envp[i++] != NULL); - } + /* Allocate kernel memory to preserve the args/env */ + saved = malloc(sizeof(args_env_t)); + BUG_ON(saved == NULL); - /* Manage the arg strings */ + memset(saved->arg_env, 0, PAGE_SIZE); - args_str_p = args_p; - __args = (char **) (args + sizeof(int)); + saved->strings_size = 0; - /* As said before, if argc is 0 (argv NULL), we put the process name (a - * kind of by-default argument) */ + /* Save args strings */ if (!argc) { - __args[0] = args_str_p; - strcpy(__args[0], current()->pcb->name); - - args_str_p += strlen(current()->pcb->name) + 1; - } - - /* Place the strings with their addresses - is the new address - * (definitive location). */ - - for (i = 0; i < argc; i++) { - __args[i] = args_str_p; - strcpy(__args[i], argv[i]); + /* If no arguments, add one with process name */ + saved->argc = 1; + strcpy(&saved->arg_env[saved->strings_size], current()->pcb->name); + saved->strings_size += strlen(current()->pcb->name) + 1; + } else { + /* Copy all arguments strings */ + saved->argc = argc; + for (i = 0; i < argc; i++) { + str_len = strlen(argv[i]) + 1; - args_str_p += strlen(argv[i]) + 1; + /* Ensure the newly copied string will not exceed the buffer size. */ + if (saved->strings_size + str_len > PAGE_SIZE) { + LOG_CRITICAL("Not enougth memory allocated\n"); - /* We check if the pointer do not exceed the page we - * allocated before */ - if (((addr_t) args_str_p - (addr_t) args) > PAGE_SIZE) { - LOG_CRITICAL("Not enougth memory allocated\n"); + free(saved); + return -ENOMEM; + } - free(args); - return -ENOMEM; + strcpy(&saved->arg_env[saved->strings_size], argv[i]); + saved->strings_size += str_len; } } - /* Environment strings */ - - /* First env. variable */ - __args = (char **) (args + sizeof(int) + sizeof(char *) * (*((int *) args))); - - /* If the environment was passed */ + /* Save env strings and count how many there are */ + saved->envc = 0; if (envp) { - i = 0; - while (envp[i] != NULL) { - __args[i] = args_str_p; - strcpy(__args[i], envp[i]); - - args_str_p += strlen(envp[i]) + 1; + do { + str_len = strlen(envp[saved->envc]) + 1; - /* We check if pointer do not exceed the page we - * allocated before. */ - if (((addr_t) args_str_p - (addr_t) args) > PAGE_SIZE) { - LOG_ERROR("Not enough memory allocated\n"); + /* Ensure the newly copied string will not exceed the buffer size. */ + if (saved->strings_size + str_len > PAGE_SIZE) { + LOG_CRITICAL("Not enougth memory allocated\n"); - free(args); + free(saved); return -ENOMEM; } - i++; - } + + strcpy(&saved->arg_env[saved->strings_size], envp[saved->envc]); + saved->strings_size += str_len; + } while (envp[saved->envc++]); } - return (addr_t) args; + return (addr_t) saved; } -void post_setup_image(void *args_env) +void post_setup_image(args_env_t *args_env, elf_img_info_t *elf_img_info) { - char **__args; + char **argv_p_base; + char *str_p; + char **env_p_base; char *args_base; - int argc, i; + int i; + elf_addr_t *aux_elf; args_base = (char *) arch_get_args_base(); - memcpy(args_base, args_env, PAGE_SIZE); + /* Save argc as first arguments */ + *((int *) args_base) = args_env->argc; + + /* Get the base address for the array of pointer for args, env and aux */ + argv_p_base = (char **) (args_base + sizeof(int)); + env_p_base = (char **) ((addr_t) argv_p_base + args_env->argc * sizeof(char *)); + /* Add one to account for the null termination of env */ + aux_elf = (elf_addr_t *) ((addr_t) env_p_base + (args_env->envc + 1) * sizeof(char *)); + + /* Adds auxiliary before args and env to get the starting address for the strings. + * Each auxiliary entry have an id and a value, the following temporary helper allows + * to easily add an entry without missing something, or adding unecessary functions. + * This is copied from linux/fs/binfmt_elf.c */ +#define NEW_AUX_ENT(id, val) \ + do { \ + *aux_elf++ = id; \ + *aux_elf++ = val; \ + } while (0) + + /* Adds up auxiliary array. */ + NEW_AUX_ENT(AT_PAGESZ, PAGE_SIZE); + NEW_AUX_ENT(AT_CLKTCK, clocksource_timer.rate); + NEW_AUX_ENT(AT_ENTRY, elf_img_info->header->e_entry); + + /* By default, MUSL will use argv[0], so this isn't required as it con */ + NEW_AUX_ENT(AT_EXECFN, 0); + + /* This should contains a bitmask of hardware capabilities */ + NEW_AUX_ENT(AT_HWCAP, HWCAP_ELF); + + /* Those value should come from elf_img_info->header, but as user application + * are compiled with linker option -N, the program header (PHDR) isn't in a PT_LOAD + * segment meaning it will not be copied in userspace. + * The main effect of this is that variable in thread local storage will not be possible. + * Those variable are *flaged* with __thread (i.e `__thread int n;`) and aren't used for now. + * This header is also used for dynamically linking, which isn't supported by SO3 anyway. + */ + NEW_AUX_ENT(AT_PHDR, 0); + NEW_AUX_ENT(AT_PHENT, 0); + NEW_AUX_ENT(AT_PHNUM, 0); - free(args_env); + /* NULL termination */ + NEW_AUX_ENT(AT_NULL, 0); - /* Now, readjust the address of the var and env strings */ + /* Remove the temporary helper. */ +#undef NEW_AUX_ENT - argc = *((int *) args_base); - __args = (char **) (args_base + sizeof(int)); + /* Copy all strings into their final destination and set pointers to them */ - /* We get the offset based on the current location and the start of the - * args_env page, then we adjust it to match our final destination. - */ - for (i = 0; i < argc; i++) - __args[i] = ((addr_t) __args[i] - (addr_t) args_env) + args_base; + /* Strings will be put just after aux */ + str_p = (char *) aux_elf; + + /* Ensure that the strings will not overflow */ + BUG_ON(args_env->strings_size + ((addr_t) str_p - (addr_t) args_base) > PAGE_SIZE); - /* Focus on the env addresses now */ - __args = (char **) (args_base + sizeof(int) + argc * sizeof(char *)); + memcpy(str_p, args_env->arg_env, args_env->strings_size); - for (i = 0; __args[i] != NULL; i++) - __args[i] = ((addr_t) __args[i] - (addr_t) args_env) + args_base; + /* Set the correct addresses into argv array */ + for (i = 0; i < args_env->argc; i++) { + argv_p_base[i] = str_p; + str_p += strlen(str_p) + 1; + } + + /* Set the correct addresses into env array */ + for (i = 0; i < args_env->envc; i++) { + env_p_base[i] = str_p; + str_p += strlen(str_p) + 1; + } + /* Ensure the NULL terminated list */ + env_p_base[args_env->envc] = NULL; + + free(args_env); } /* @@ -543,7 +557,7 @@ int setup_proc_image_replace(elf_img_info_t *elf_img_info, pcb_t *pcb, int argc, { uint32_t page_count; addr_t ret; - void *__args_env; + args_env_t *__args_env; /* FIXME: detect fragmented executable (error)? */ /* @@ -558,7 +572,7 @@ int setup_proc_image_replace(elf_img_info_t *elf_img_info, pcb_t *pcb, int argc, if (ret < 0) return ret; - __args_env = (void *) ret; + __args_env = (args_env_t *) ret; /* Reset the process stack and page count */ reset_process_stack(pcb); @@ -618,7 +632,7 @@ int setup_proc_image_replace(elf_img_info_t *elf_img_info, pcb_t *pcb, int argc, /* Prepare the arguments within the page reserved for this purpose. */ if (__args_env) - post_setup_image(__args_env); + post_setup_image(__args_env, elf_img_info); return 0; } diff --git a/so3/kernel/thread.c b/so3/kernel/thread.c index 2f4208b52..14873b6ff 100644 --- a/so3/kernel/thread.c +++ b/so3/kernel/thread.c @@ -270,7 +270,7 @@ void thread_exit(int exit_status) if (pcb && current() != pcb->main_thread) { if (current()->clear_child_tid) { *(current()->clear_child_tid) = 0; - do_futex(current()->clear_child_tid, FUTEX_WAKE, 1, NULL, NULL, 0, 0); + sys_do_futex(current()->clear_child_tid, FUTEX_WAKE, 1, NULL, NULL, 0); } }