From c685a37c15adcecf9ba600c81894b4f8e993caca Mon Sep 17 00:00:00 2001 From: EnderIce2 Date: Fri, 19 Jan 2024 06:45:44 +0200 Subject: [PATCH] Update userspace --- .vscode/launch.json | 6 +- Doxyfile | 6 +- Makefile | 3 + apps/base/Makefile | 7 + apps/base/cross_test/Makefile | 16 + apps/base/cross_test/expected_glibc.txt | 22 + apps/base/cross_test/linux_glibc.c | 9 + apps/base/cross_test/win_mingw.c | 9 + apps/base/echo/echo.c | 56 +- apps/base/fsh/fsh.c | 157 ++++- apps/base/utest/Makefile | 66 ++ apps/base/utest/userspace_test.c | 810 ++++++++++++++++++++++++ apps/system/init/aux.h | 69 -- apps/system/init/init.c | 99 +-- 14 files changed, 1154 insertions(+), 181 deletions(-) create mode 100644 apps/base/cross_test/Makefile create mode 100644 apps/base/cross_test/expected_glibc.txt create mode 100644 apps/base/cross_test/linux_glibc.c create mode 100644 apps/base/cross_test/win_mingw.c create mode 100644 apps/base/utest/Makefile create mode 100644 apps/base/utest/userspace_test.c delete mode 100644 apps/system/init/aux.h diff --git a/.vscode/launch.json b/.vscode/launch.json index c863080..3bcd82d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -35,7 +35,7 @@ "description": "Load binary." }, { - "text": "add-symbol-file ${workspaceFolder}/../Kernel/kernel.fsys", + "text": "add-symbol-file ${workspaceFolder}/../Kernel/fennix.elf", "description": "Load kernel binary." }, ] @@ -74,7 +74,7 @@ "description": "Load binary." }, { - "text": "add-symbol-file ${workspaceFolder}/../Kernel/kernel.fsys", + "text": "add-symbol-file ${workspaceFolder}/../Kernel/fennix.elf", "description": "Load kernel binary." }, ] @@ -113,7 +113,7 @@ "description": "Load binary." }, { - "text": "add-symbol-file ${workspaceFolder}/../Kernel/kernel.fsys", + "text": "add-symbol-file ${workspaceFolder}/../Kernel/fennix.elf", "description": "Load kernel binary." }, ] diff --git a/Doxyfile b/Doxyfile index 76a8caf..c0c95b7 100644 --- a/Doxyfile +++ b/Doxyfile @@ -44,7 +44,7 @@ PROJECT_NUMBER = 1.0.0 # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "Fennix Userspace Documentation" +PROJECT_BRIEF = "Userspace Documentation" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -864,7 +864,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = Userspace tools/Doxygen_README.md +INPUT = Userspace tools/doxymds/userspace.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1064,7 +1064,7 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = tools/Doxygen_README.md +USE_MDFILE_AS_MAINPAGE = tools/doxymds/userspace.md #--------------------------------------------------------------------------- # Configuration options related to source browsing diff --git a/Makefile b/Makefile index 30ec525..92aaf5b 100644 --- a/Makefile +++ b/Makefile @@ -31,12 +31,15 @@ MUSL_CONFIGURE_FLAGS += --enable-debug endif build_musl: +ifeq ($(wildcard cache/musl),) mkdir -p cache/musl cd cache/musl && \ ../../musl/configure --prefix=$(PREFIX) \ --target=$(TARGET) --includedir=$(PREFIX)/include \ $(MUSL_CONFIGURE_FLAGS) make -C cache/musl -j$(shell nproc) +endif + $(info Installing musl libc) cd cache/musl && make TARGET=$(TARGET) install cp out/lib/crt1.o out/lib/crt0.o cd out/lib && ln -s /lib/libc.so ./ld-musl-x86_64.so.1 && \ diff --git a/apps/base/Makefile b/apps/base/Makefile index e57f0bc..05a94b0 100644 --- a/apps/base/Makefile +++ b/apps/base/Makefile @@ -1,3 +1,6 @@ +# Config file +include ../../../Makefile.conf + cwd := $(CURDIR) CACHE_DIR := $(cwd)/../../cache PREFIX := $(cwd)/../../out/ @@ -50,9 +53,13 @@ endif build: make -C echo build make -C fsh build + make -C utest build + make -C cross_test build # $(MAKE) build_busybox # $(MAKE) build_bash clean: make -C echo clean make -C fsh clean + make -C utest clean + make -C cross_test clean diff --git a/apps/base/cross_test/Makefile b/apps/base/cross_test/Makefile new file mode 100644 index 0000000..ebd3f0a --- /dev/null +++ b/apps/base/cross_test/Makefile @@ -0,0 +1,16 @@ +WORKSPACE := ../../../ + +# Config file +include ../$(WORKSPACE)Makefile.conf + +FILENAME = utest_ + +CFLAGS = -static -g -ggdb3 -O0 + +build: + $(info Compiling $(FILENAME)linux) + gcc linux_glibc.c $(CFLAGS) -o $(WORKSPACE)out/bin/$(FILENAME)linux + $(info Compiling $(FILENAME)win) + x86_64-w64-mingw32-gcc win_mingw.c $(CFLAGS) -o $(WORKSPACE)out/bin/$(FILENAME)win.exe + +clean: diff --git a/apps/base/cross_test/expected_glibc.txt b/apps/base/cross_test/expected_glibc.txt new file mode 100644 index 0000000..697392c --- /dev/null +++ b/apps/base/cross_test/expected_glibc.txt @@ -0,0 +1,22 @@ +$ setarch `uname -m` -R strace ./utest_linux + +execve("./utest_linux", ["./utest_linux"], 0x7fffffffdec0 /* 56 vars */) = 0 +arch_prctl(0x3001 /* ARCH_??? */, 0x7fffffffde40) = -1 EINVAL (Invalid argument) +brk(NULL) = 0x4cd000 +brk(0x4cddc0) = 0x4cddc0 +arch_prctl(ARCH_SET_FS, 0x4cd3c0) = 0 +set_tid_address(0x4cd690) = 68565 +set_robust_list(0x4cd6a0, 24) = 0 +rseq(0x4cdd60, 0x20, 0, 0x53053053) = 0 +uname({sysname="Fennix", nodename="fennix", ...}) = 0 +prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 +readlink("/proc/self/exe", "/bin/utest_linux"..., 4096) = 50 +getrandom("\x1e\x3c\x20\xdd\x09\xe8\x46\x0d", 8, GRND_NONBLOCK) = 8 +brk(0x4eedc0) = 0x4eedc0 +brk(0x4ef000) = 0x4ef000 +mprotect(0x4c1000, 16384, PROT_READ) = 0 +newfstatat(1, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}, AT_EMPTY_PATH) = 0 +write(1, "Hello, World!\n", 14Hello, World! +) = 14 +exit_group(0) = ? ++++ exited with 0 +++ diff --git a/apps/base/cross_test/linux_glibc.c b/apps/base/cross_test/linux_glibc.c new file mode 100644 index 0000000..cf01af1 --- /dev/null +++ b/apps/base/cross_test/linux_glibc.c @@ -0,0 +1,9 @@ +#include +#include + +int main(int argc, char *argv[], char *envp[]) +{ + printf("glibc: Hello, World!\n"); + fflush(stdout); + return 0; +} diff --git a/apps/base/cross_test/win_mingw.c b/apps/base/cross_test/win_mingw.c new file mode 100644 index 0000000..59d526c --- /dev/null +++ b/apps/base/cross_test/win_mingw.c @@ -0,0 +1,9 @@ +#include +#include + +int main(int argc, char *argv[], char *envp[]) +{ + printf("mingw: Hello, World!\n"); + fflush(stdout); + return 0; +} diff --git a/apps/base/echo/echo.c b/apps/base/echo/echo.c index 5231cb9..ce3b8e8 100644 --- a/apps/base/echo/echo.c +++ b/apps/base/echo/echo.c @@ -1,15 +1,59 @@ #include +#include +#include +#include int main(int argc, char *argv[]) { - if (argc == 1) + int enableEscapes = 0; + int disableNewline = 0; + + int opt; + while ((opt = getopt(argc, argv, "neE")) != -1) { - printf("Usage: echo [args]\n"); - return 0; + switch (opt) + { + case 'n': + disableNewline = 1; + break; + case 'e': + enableEscapes = 1; + break; + case 'E': + enableEscapes = 0; + break; + case '?': + default: + exit(EXIT_FAILURE); + } } - for (int i = 1; i < argc; i++) - printf("%s ", argv[i]); - printf("\n"); + (void)enableEscapes; + + size_t totalLength = 0; + for (int i = optind; i < argc; ++i) + totalLength += strlen(argv[i]) + 1; + + char *result = (char *)malloc(totalLength); + if (!result) + { + perror("Memory allocation error"); + exit(EXIT_FAILURE); + } + result[0] = '\0'; + + for (int i = optind; i < argc; ++i) + { + strcat(result, argv[i]); + if (i < argc - 1) + strcat(result, " "); + } + + printf("%s", result); + + if (!disableNewline) + printf("\n"); + + free(result); return 0; } diff --git a/apps/base/fsh/fsh.c b/apps/base/fsh/fsh.c index 38ab078..bbad70c 100644 --- a/apps/base/fsh/fsh.c +++ b/apps/base/fsh/fsh.c @@ -1,7 +1,160 @@ #include +#include -int main(int argc, char *argv[], char *envp[]) +#include +#include +#include +#define _GNU_SOURCE +#include +#include +#define _POSIX_SOURCE +#include + +#define MAX_COMMAND_LENGTH 1024 +#define MAX_ARGS 10 + +void DisableInputBuffering() { - printf("Hello, world!\n"); + struct termios tty_attr; + tcgetattr(STDIN_FILENO, &tty_attr); + tty_attr.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &tty_attr); +} + +void EnableInputBuffering() +{ + struct termios tty_attr; + tcgetattr(STDIN_FILENO, &tty_attr); + tty_attr.c_lflag |= ICANON | ECHO; + tcsetattr(STDIN_FILENO, TCSANOW, &tty_attr); +} + +void ReadLine(char *Buffer, size_t BufferSize) +{ + size_t index = 0; + int c; + while (1) + { + c = getchar(); + if (c == EOF || c == '\n') + { + Buffer[index] = '\0'; + break; + } + else if (c == 127 || c == 8) // Backspace + { + if (index > 0) + { + printf("\b \b"); + index--; + } + } + else if (c == 4) // Ctrl + D + { + if (index == 0) + kill(getpid(), SIGQUIT); + else + putchar('\a'); + } + else + { + if (index < BufferSize - 1) + { + putchar(c); + Buffer[index] = (char)c; + index++; + } + } + } +} + +void ExecuteCommand(char *command) +{ + char *args[MAX_ARGS]; + int i = 0; + + char *token = strtok(command, " "); + while (token != NULL && i < MAX_ARGS - 1) + { + args[i++] = token; + token = strtok(NULL, " "); + } + args[i] = NULL; + + pid_t pid = fork(); + if (pid == 0) + { + execvp(args[0], args); + perror("execvp"); + exit(EXIT_FAILURE); + } + else if (pid > 0) + wait(NULL); + else + { + perror("fork"); + exit(EXIT_FAILURE); + } +} + +void HandleSignal(int signal) +{ + if (signal == SIGQUIT) + { + EnableInputBuffering(); + printf("\n"); + exit(EXIT_SUCCESS); + } + else if (signal == SIGINT) + { + putchar('\n'); + } + else if (signal == SIGHUP) + { + EnableInputBuffering(); + exit(EXIT_SUCCESS); + } + else if (signal == SIGCHLD) + wait(NULL); + else if (signal == SIGSEGV) + { + printf("Segmentation fault\n"); + while (1) + sleep(1000); + } + else + { + printf("Signal %s(%d) received\n", + strsignal(signal), signal); + } +} + +int main() +{ + char command[MAX_COMMAND_LENGTH]; + for (int i = 0; i < NSIG; ++i) + signal(i, HandleSignal); + + char hostname[256]; + gethostname(hostname, sizeof(hostname)); + + while (1) + { + printf("\033[1;32m"); + printf("┌──(%s@%s)-[%s]\n", getenv("USER"), hostname, getenv("PWD")); + printf("└$ "); + printf("\033[0m"); + + DisableInputBuffering(); + ReadLine(command, sizeof(command)); + EnableInputBuffering(); + putchar('\n'); + + if (strcmp(command, "exit") == 0) + break; + + ExecuteCommand(command); + } + return 0; } diff --git a/apps/base/utest/Makefile b/apps/base/utest/Makefile new file mode 100644 index 0000000..52122a4 --- /dev/null +++ b/apps/base/utest/Makefile @@ -0,0 +1,66 @@ +WORKSPACE := ../../../ + +# Config file +include ../$(WORKSPACE)Makefile.conf + +CC = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)as +OBJDUMP = ../$(WORKSPACE)$(COMPILER_PATH)/$(COMPILER_ARCH)objdump + +GIT_COMMIT = $(shell git rev-parse HEAD) +GIT_COMMIT_SHORT = $(shell git rev-parse --short HEAD) + +ifeq ($(OSARCH), amd64) +S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") +C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") +else ifeq ($(OSARCH), i386) +S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") +C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") +else ifeq ($(OSARCH), aarch64) +S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") +C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") +endif +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) + +SYSROOT = --sysroot=$(WORKSPACE)out/ +FILENAME = utest + +HEADERS = $(sort $(dir $(wildcard $(WORKSPACE)out/include/*))) + +LDFLAGS = +CFLAGS = -I$(WORKSPACE)out/include \ + -DGIT_COMMIT='"$(GIT_COMMIT)"' \ + -DGIT_COMMIT_SHORT='"$(GIT_COMMIT_SHORT)"' +WARNCFLAG = -Wall -Wextra + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fverbose-asm + LDFLAGS += -ggdb3 -O0 +endif + +build: $(FILENAME) + mv $(FILENAME) $(WORKSPACE)out/bin/$(FILENAME) + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(SYSROOT) $(OBJ) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ -fno-rtti + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f $(OBJ) diff --git a/apps/base/utest/userspace_test.c b/apps/base/utest/userspace_test.c new file mode 100644 index 0000000..1056570 --- /dev/null +++ b/apps/base/utest/userspace_test.c @@ -0,0 +1,810 @@ +#define _POSIX_C_SOURCE 200809L +#define _POSIX_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define _GNU_SOURCE +#include +#include +#include + +#define AT_NULL 0 +#define AT_IGNORE 1 +#define AT_EXECFD 2 +#define AT_PHDR 3 +#define AT_PHENT 4 +#define AT_PHNUM 5 +#define AT_PAGESZ 6 +#define AT_BASE 7 +#define AT_FLAGS 8 +#define AT_ENTRY 9 +#define AT_NOTELF 10 +#define AT_UID 11 +#define AT_EUID 12 +#define AT_GID 13 +#define AT_EGID 14 +#define AT_PLATFORM 15 +#define AT_HWCAP 16 +#define AT_CLKTCK 17 +#define AT_SECURE 23 +#define AT_BASE_PLATFORM 24 +#define AT_RANDOM 25 +#define AT_HWCAP2 26 +#define AT_EXECFN 31 +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 +#define AT_L1I_CACHESIZE 40 +#define AT_L1I_CACHEGEOMETRY 41 +#define AT_L1D_CACHESIZE 42 +#define AT_L1D_CACHEGEOMETRY 43 +#define AT_L2_CACHESIZE 44 +#define AT_L2_CACHEGEOMETRY 45 +#define AT_L3_CACHESIZE 46 +#define AT_L3_CACHEGEOMETRY 47 +#define AT_MINSIGSTKSZ 51 + +#define EVENT_SIZE (sizeof(struct inotify_event)) +#define BUF_LEN (1024 * (EVENT_SIZE + 16)) + +typedef struct +{ + uint32_t a_type; + union + { + uint32_t a_val; + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + uint64_t a_type; + union + { + uint64_t a_val; + } a_un; +} Elf64_auxv_t; + +#ifdef __LP64__ +#define Elf_auxv_t Elf64_auxv_t +#else +#define Elf_auxv_t Elf32_auxv_t +#endif + +#define loop \ + while (1) \ + ; + +#define __musl_SYS_brk 12 + +static __inline long __musl_syscall1(long n, long a1) +{ + unsigned long ret; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(n), "D"(a1) + : "rcx", "r11", "memory"); + return ret; +} + +long __musl_syscall_ret(unsigned long r) +{ + if (r > -4096UL) + { + errno = -r; + return -1; + } + return r; +} + +struct passwd *p = NULL; + +void test_passwd() +{ + printf("- Testing passwd\n"); + + p = getpwuid(getuid()); + if (p == NULL) + { + perror("getpwuid"); + fflush(stdout); + fflush(stderr); + } + else + { + printf("name: %s\n", p->pw_name); + printf("passwd: %s\n", p->pw_passwd); + printf("uid: %d\n", p->pw_uid); + printf("gid: %d\n", p->pw_gid); + printf("gecos: %s\n", p->pw_gecos); + printf("dir: %s\n", p->pw_dir); + printf("shell: %s\n", p->pw_shell); + } +} + +static void *heap_end = NULL; +void *__libc_sbrk(intptr_t increment) +{ + if (heap_end == NULL) + { + heap_end = (void *)__musl_syscall1(__musl_SYS_brk, 0); + printf("SYS_brk(0) = %p\n", heap_end); + + heap_end = (void *)__musl_syscall1(__musl_SYS_brk, (long)heap_end + 0x2000); + printf("Adding 0x2000 to heap_end: %p\n", heap_end); + } + + if (increment == 0) + { + printf("Returning heap_end: %p\n", heap_end); + return heap_end; + } + + void *old_heap_end = heap_end; + + long ret = __musl_syscall1(__musl_SYS_brk, (long)heap_end + increment); + printf("SYS_brk(%p) = %p\n", (void *)((long)heap_end + increment), (void *)ret); + if (ret == 0) + { + heap_end = (void *)__musl_syscall1(__musl_SYS_brk, 0); + printf("SYS_brk(0) = %p\n", heap_end); + return old_heap_end; + } + + return (void *)__musl_syscall_ret(ret); +} + +void test_brk() +{ + printf("- Testing brk\n"); + + void *current_brk = __libc_sbrk(0); + printf("Initial break address: %p\n", current_brk); + + void *new_brk = __libc_sbrk(0x1000); + if (new_brk == (void *)-1) + { + perror("Error extending heap"); + fflush(stdout); + fflush(stderr); + return; + } + printf("Heap extended successfully.\n"); + + printf("New break address: %p\n", new_brk); + + if (__libc_sbrk(current_brk - new_brk) == (void *)-1) + { + perror("Error reducing heap"); + fflush(stdout); + fflush(stderr); + return; + } + + printf("Heap reduced successfully.\n"); + printf("Current break address after reduction: %p\n", __libc_sbrk(0)); + + void *extended_brk = __libc_sbrk(0x1000); + if (extended_brk == (void *)-1) + { + perror("Error extending heap after reduction"); + fflush(stdout); + fflush(stderr); + return; + } + + printf("Heap extended successfully after reduction.\n"); + printf("New break address after extension: %p\n", extended_brk); + + __libc_sbrk(-0x1000); +} + +void test_time() +{ + printf("- Testing time\n"); + + struct tm time = {0}; + int Year = 2024; + int Month = 1; + int Day = 1; + int Hour = 0; + int Minute = 0; + int Second = 0; + + time.tm_year = Year - 1900; + time.tm_mon = Month - 1; + time.tm_mday = Day; + time.tm_hour = Hour; + time.tm_min = Minute; + time.tm_sec = Second; + + if (time.tm_year < 0) + time.tm_year = 0; + + time_t t = mktime(&time); + if (t != (time_t)-1) + stime(&t); + else + { + perror("mktime"); + fflush(stdout); + fflush(stderr); + } +} + +void test_args(int argc, char *argv[], char *envp[]) +{ + printf("- Testing args\n"); + + printf("%p %p %p\n", + (void *)(uintptr_t)&argc, + (void *)&argv, + (void *)&envp); + + printf("I have %d arguments\n", argc); + for (int i = 0; i < argc; i++) + printf("argv[%d] = (%p) %s\n", i, argv[i], argv[i]); + + int envc = 0; + while (envp[envc] != NULL) + envc++; + + printf("I have %d environment variables\n", envc); + for (int i = 0; i < envc; i++) + printf("envp[%d] = (%p) %s\n", i, envp[i], envp[i]); + + Elf64_auxv_t *auxv; + char **e = envp; + + while (*e++ != NULL) + ; + + for (auxv = (Elf64_auxv_t *)e; auxv->a_type != AT_NULL; auxv++) + printf("auxv: %ld %#lx\n", auxv->a_type, auxv->a_un.a_val); +} + +void test_stdio() +{ + printf("- Testing stdin\n"); + + pid_t pid = fork(); + if (pid < 0) + { + perror("fork"); + fflush(stdout); + fflush(stderr); + return; + } + else if (pid != 0) + return; + + printf("Type a character: "); + char c = getchar(); + printf("You typed: %c (%#x)\n", c, c); + char *line = NULL; + size_t len = 0; + ssize_t read; + printf("Type a line: "); + read = getline(&line, &len, stdin); + printf("You typed: %s (%ld bytes)\n", line, read); + free(line); + exit(EXIT_SUCCESS); +} + +void test_file() +{ + printf("- Testing file operations\n"); + + FILE *test = fopen("/etc/passwd", "r"); + if (test == NULL) + { + perror("fopen"); + fflush(stdout); + fflush(stderr); + return; + } + + printf("/etc/passwd contents: "); + char ch; + while (1) + { + ch = fgetc(test); + if (ch == EOF) + { + printf("\n"); + break; + } + putchar(ch); + } + fclose(test); +} + +void test_ptmx() +{ + printf("- Testing PTMX\n"); + + int master, slave; + char buffer[256]; + + if (openpty(&master, &slave, NULL, NULL, NULL) == -1) + { + perror("openpty"); + fflush(stdout); + fflush(stderr); + return; + } + + write(master, "Hello, pty!\n", 12); + ssize_t bytesRead = read(slave, buffer, sizeof(buffer)); + if (bytesRead > 0) + { + buffer[bytesRead] = '\0'; + printf("Received from pty: %s", buffer); + } + + close(master); + close(slave); +} + +void test_system() +{ + printf("- Testing system()\n"); + int ret = system("echo Hello, world!"); + printf("system() returned %d\n", ret); +} + +int sigRec = 0; +void signalHandler(int signo) +{ + if (signo == SIGUSR1) + printf("Signal received\n"); + else + printf("Unknown signal received %d\n", signo); + sigRec = 1; +} + +void test_signal() +{ + printf("- Testing Signals\n"); + signal(SIGUSR1, signalHandler); + signal(SIGUSR2, signalHandler); + printf("Sending SIGUSR1...\n"); + kill(getpid(), SIGUSR1); + printf("Sending SIGUSR2...\n"); + kill(getpid(), SIGUSR2); + printf("Signal sent\n"); + while (!sigRec) + sleep(1); +} + +void test_execve_fork() +{ + printf("- Testing execve and fork\n"); + + pid_t pid = fork(); + if (pid == 0) // Child process + { + pid_t pid2 = fork(); + if (pid2 == 0) // Child process + { + char *shebangArgs[] = {"/test.sh", NULL}; + execv(shebangArgs[0], shebangArgs); + perror("execv"); + fflush(stdout); + fflush(stderr); + exit(EXIT_SUCCESS); + } + + printf("Creating shell process\n"); + char *args[] = {"/bin/echo", "Hello World from echo!", NULL}; + execv(args[0], args); + perror("execv"); + fflush(stdout); + fflush(stderr); + exit(EXIT_SUCCESS); + } + else if (pid > 0) + { + printf("Waiting for child process %d to exit\n", pid); + int status; + waitpid(pid, &status, 0); + printf("status=%#x\n", status); + int exited = WIFEXITED(status); + if (exited) + { + int exitCode = WEXITSTATUS(status); + if (exitCode != 0) + printf("Child process exited with code: %d\n", exitCode); + } + else + { + printf("Execution failed. (exited=%d, status=%#x)\n", exited, status); + return; + } + } + else + { + perror("fork"); + fflush(stdout); + fflush(stderr); + return; + } +} + +void test_dirent() +{ + printf("- Testing dirent\n"); + DIR *dir; + struct dirent *entry; + + dir = opendir("/etc"); + if (dir == NULL) + { + perror("opendir"); + fflush(stdout); + fflush(stderr); + return; + } + + printf("Contents of the directory:\n"); + + int i = 0; + while ((entry = readdir(dir)) != NULL) + { + printf("%s ", entry->d_name); + if (++i % 5 == 0) + printf("\n"); + } + + closedir(dir); +} + +char *create_file() +{ + FILE *fp; + char *path; + + if (p == NULL) + { + path = malloc(20); + sprintf(path, "/watched_file.txt"); + fp = fopen(path, "w"); + } + else + { + path = malloc(strlen(p->pw_dir) + 20); + sprintf(path, "%s/watched_file.txt", p->pw_dir); + fp = fopen(path, "w"); + } + + if (fp == NULL) + { + perror("fopen"); + fflush(stdout); + fflush(stderr); + return path; + } + fclose(fp); + return path; +} + +char *create_directory() +{ + char *path; + + if (p == NULL) + { + path = malloc(20); + sprintf(path, "/watched_directory"); + } + else + { + path = malloc(strlen(p->pw_dir) + 20); + sprintf(path, "%s/watched_directory", p->pw_dir); + } + + if (mkdir(path, 0777) < 0) + { + perror("mkdir"); + fflush(stdout); + fflush(stderr); + return path; + } + return path; +} + +void test_watch_file() +{ + printf("- Testing file watching\n"); + pid_t pid = fork(); + if (pid < 0) + { + perror("fork"); + fflush(stdout); + fflush(stderr); + return; + } + else if (pid != 0) + return; + + char *path = create_file(); + int fd, wd; + char *buffer = malloc(BUF_LEN); + + fd = inotify_init(); + if (fd < 0) + { + perror("inotify_init"); + exit(EXIT_FAILURE); + } + + wd = inotify_add_watch(fd, path, + IN_OPEN | IN_MODIFY | IN_CLOSE); + if (wd < 0) + { + perror("inotify_add_watch"); + exit(EXIT_FAILURE); + } + + printf("Watching for changes in file.txt...\n"); + + while (1) + { + ssize_t len = read(fd, buffer, BUF_LEN); + if (len < 0) + { + perror("read"); + exit(EXIT_FAILURE); + } + + struct inotify_event *event = (struct inotify_event *)buffer; + if (event->mask & IN_MODIFY) + printf("File modified!\n"); + else if (event->mask & IN_OPEN) + printf("File opened!\n"); + else if (event->mask & IN_CLOSE) + printf("File closed!\n"); + else + printf("Unknown event!\n"); + } + + inotify_rm_watch(fd, wd); + close(fd); + exit(EXIT_SUCCESS); +} + +void test_watch_directory() +{ + printf("- Testing directory watching\n"); + pid_t pid = fork(); + if (pid < 0) + { + perror("fork"); + fflush(stdout); + fflush(stderr); + return; + } + else if (pid != 0) + return; + + char *path = create_directory(); + int fd, wd; + char *buffer = malloc(BUF_LEN); + + fd = inotify_init(); + if (fd < 0) + { + perror("inotify_init"); + exit(EXIT_FAILURE); + } + + wd = inotify_add_watch(fd, path, + IN_OPEN | IN_CREATE | IN_DELETE | IN_MODIFY); + if (wd < 0) + { + perror("inotify_add_watch"); + exit(EXIT_FAILURE); + } + + printf("Watching for changes in the directory...\n"); + while (1) + { + ssize_t len = read(fd, buffer, BUF_LEN); + if (len < 0) + { + perror("read"); + exit(EXIT_FAILURE); + } + + struct inotify_event *event = (struct inotify_event *)buffer; + if (event->mask & IN_CREATE) + printf("File created: %s\n", event->name); + else if (event->mask & IN_DELETE) + printf("File deleted: %s\n", event->name); + else if (event->mask & IN_MODIFY) + printf("File modified: %s\n", event->name); + else if (event->mask & IN_OPEN) + printf("File opened: %s\n", event->name); + else + printf("Unknown event!\n"); + } + + inotify_rm_watch(fd, wd); + close(fd); + exit(EXIT_SUCCESS); +} + +void shutdown_linux() +{ + printf("- Testing shutdown\n"); + sync(); + reboot(RB_POWER_OFF); +} + +void reboot_linux() +{ + printf("- Testing reboot\n"); + sync(); + reboot(RB_AUTOBOOT); +} + +void self_fork_exec() +{ + while (1) + { + int pid = fork(); + + // if (pid >= 10) + // { + // printf("[%d] Forked %d times, exiting...\n", getpid(), pid); + // kill(getpid(), SIGTERM); + // } + + if (pid != 0) + continue; + + printf("[%d] Executing utest(%d)...\n", getpid(), getppid()); + char *a[] = {"/bin/utest", "loop", NULL}; + int b = execv(a[0], a); + printf("Failed to execute utest: %d\n", b); + } +} + +void fork_bomb() +{ + while (1) + { + printf("[%d] Forking...\n", getpid()); + int pid = fork(); + if (pid != 0) + printf("[%d] Forked\n", pid); + else + printf("[%d] Child\n", getpid()); + sleep(5); + } +} + +void fork_bomb_syscall() +{ +#ifdef __x86_64__ + while (1) + { + printf("[%d] Forking...\n", getpid()); + unsigned long pid; + __asm__ __volatile__("syscall" : "=a"(pid) + : "a"(57 /* x86_64 SYS_fork */) + : "rcx", "r11", "memory"); + if (pid != 0) + printf("[%ld] Forked\n", pid); + else + printf("[%d] Child\n", getpid()); + sleep(5); + } +#endif +} + +int main(int argc, char *argv[], char *envp[]) +{ + if (argv[1] != NULL && strcmp(argv[1], "loop") == 0) + { + printf("[%d] Looping...\n", getpid()); + while (1) + ; + } + + printf("- Testing userspace...\n"); + + // printf("Press RETURN to start tests...\n"); + // char key = 0; + // while (key != '\n' && key != '\r') + // key = getchar(); + + // self_fork_exec(); + // fork_bomb(); + // fork_bomb_syscall(); + + test_passwd(); + test_brk(); + test_time(); + test_signal(); + test_ptmx(); + test_args(argc, argv, envp); + // test_stdio(); + test_system(); + test_file(); + test_dirent(); + test_execve_fork(); + test_watch_file(); + test_watch_directory(); + // shutdown_linux(); + // reboot_linux(); + + int status = 0; + pid_t wpid; + + printf("Waiting for child processes to exit...\n"); + while ((wpid = wait(&status)) > 0) + sleep(2); + + sync(); + printf("Userspace tests complete!\n"); + + pid_t pid = fork(); + if (pid < 0) + { + perror("fork"); + fflush(stdout); + fflush(stderr); + return 1; + } + else if (pid == 0) + { + printf("Starting utest_linux...\n"); + char *args[] = {"/bin/utest_linux", NULL}; + int ret = execv(args[0], args); + perror("execv"); + fflush(stdout); + fflush(stderr); + return ret; + } + + waitpid(pid, &status, 0); + + // check if exited normally, or crashed + if (WIFSIGNALED(status)) + { + int signal = WTERMSIG(status); + printf("utest_linux exited with signal: %s\n", strsignal(signal)); + return signal; + } + else if (WIFEXITED(status)) + { + int exitCode = WEXITSTATUS(status); + if (exitCode != 0) + { + printf("utest_linux exited with code: %d\n", exitCode); + return exitCode; + } + } + else + { + printf("utest_linux exited abnormally\n"); + return 1; + } + return 0; +} diff --git a/apps/system/init/aux.h b/apps/system/init/aux.h deleted file mode 100644 index ef1d758..0000000 --- a/apps/system/init/aux.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef __FENNIX_LIBC_AUX_H__ -#define __FENNIX_LIBC_AUX_H__ - -#include - -#define AT_NULL 0 -#define AT_IGNORE 1 -#define AT_EXECFD 2 -#define AT_PHDR 3 -#define AT_PHENT 4 -#define AT_PHNUM 5 -#define AT_PAGESZ 6 -#define AT_BASE 7 -#define AT_FLAGS 8 -#define AT_ENTRY 9 -#define AT_NOTELF 10 -#define AT_UID 11 -#define AT_EUID 12 -#define AT_GID 13 -#define AT_EGID 14 -#define AT_PLATFORM 15 -#define AT_HWCAP 16 -#define AT_CLKTCK 17 -#define AT_SECURE 23 -#define AT_BASE_PLATFORM 24 -#define AT_RANDOM 25 -#define AT_HWCAP2 26 -#define AT_EXECFN 31 -#define AT_SYSINFO 32 -#define AT_SYSINFO_EHDR 33 -#define AT_L1I_CACHESHAPE 34 -#define AT_L1D_CACHESHAPE 35 -#define AT_L2_CACHESHAPE 36 -#define AT_L3_CACHESHAPE 37 -#define AT_L1I_CACHESIZE 40 -#define AT_L1I_CACHEGEOMETRY 41 -#define AT_L1D_CACHESIZE 42 -#define AT_L1D_CACHEGEOMETRY 43 -#define AT_L2_CACHESIZE 44 -#define AT_L2_CACHEGEOMETRY 45 -#define AT_L3_CACHESIZE 46 -#define AT_L3_CACHEGEOMETRY 47 -#define AT_MINSIGSTKSZ 51 - -typedef struct -{ - uint32_t a_type; - union - { - uint32_t a_val; - } a_un; -} Elf32_auxv_t; - -typedef struct -{ - uint64_t a_type; - union - { - uint64_t a_val; - } a_un; -} Elf64_auxv_t; - -#ifdef __LP64__ -#define Elf_auxv_t Elf64_auxv_t -#else -#define Elf_auxv_t Elf32_auxv_t -#endif - -#endif // !__FENNIX_LIBC_AUX_H__ diff --git a/apps/system/init/init.c b/apps/system/init/init.c index 2377842..03ac27b 100644 --- a/apps/system/init/init.c +++ b/apps/system/init/init.c @@ -1,103 +1,6 @@ -#include -#include -#include -#include #include -#include "aux.h" -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -void test_args(int argc, char *argv[], char *envp[]) +int main(int, char *[], char *[]) { - printf("%p %p %p\n", (void *)(uint64_t)&argc, (void *)&argv, (void *)&envp); - printf("I have %d arguments\n", argc); - for (int i = 0; i < argc; i++) - printf("argv[%d] = (%p) %s\n", i, argv[i], argv[i]); - - int envc = 0; - while (envp[envc] != NULL) - envc++; - - printf("I have %d environment variables\n", envc); - for (int i = 0; i < envc; i++) - printf("envp[%d] = (%p) %s\n", i, envp[i], envp[i]); - - Elf64_auxv_t *auxv; - char **e = envp; - - while (*e++ != NULL) - ; - - for (auxv = (Elf64_auxv_t *)e; auxv->a_type != AT_NULL; auxv++) - printf("auxv: %ld %#lx\n", auxv->a_type, auxv->a_un.a_val); -} - -int main(int argc, char *argv[], char *envp[]) -{ - printf("Hello, World!\n"); - // while (1); - // test_args(argc, argv, envp); - FILE *test = fopen("/test.txt", "r"); - if (test == NULL) - { - printf("Failed to open file\n"); - return -0xF11e; - } - - printf("Test.txt contents: "); - char ch; - while (1) - { - ch = fgetc(test); - if (ch == EOF) - { - printf("\n"); - break; - } - putchar(ch); - } - fclose(test); - - pid_t pid = fork(); - - if (pid == 0) // Child process - { - pid_t pid2 = fork(); - if (pid == 0) // Child process - { - char *shebang_args[] = {"/test.sh", NULL}; - execv(shebang_args[0], shebang_args); - } - - printf("Creating shell process\n"); - char *args[] = {"/bin/echo", "Hello, World!", NULL}; - execv(args[0], args); - exit(EXIT_FAILURE); - } - else if (pid > 0) - { - printf("Waiting for child process %d to exit\n", pid); - int status; - wait(&status); - int exited = WIFEXITED(status); - if (exited) - { - int exit_code = WEXITSTATUS(status); - printf("Child process exited with code: %d\n", exit_code); - return exit_code; - } - else - { - printf("Execution failed. (%d)\n", exited); - exit(EXIT_FAILURE); - } - } - else - { - printf("\eFF0000Failed to create the process.\n"); - exit(EXIT_FAILURE); - } - return 0; }