diff --git a/CMakeLists.txt b/CMakeLists.txt index b8afa62..0ceab63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,7 @@ if(MCOPY_EXE AND MKFS_EXE AND CPIO_EXE) COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR} COMMAND sh -c "find . -mindepth 1 ! -name '*.cpio' -print0 | ${CPIO_EXE} --null -ov -H newc > \"${INITRAMFS_CPIO_FILE}\"" WORKING_DIRECTORY ${INITRAMFS_SRC_DIR} - DEPENDS ${INIT_FILES} init_elf + DEPENDS ${INIT_FILES} init debug VERBATIM COMMENT "Packing initramfs to cpio..." ) diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 48c777e..b8f8e0e 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -23,6 +23,7 @@ file(GLOB_RECURSE KERNEL_SOURCES CMAKE_CONFIGURE_DEPENDS "src/mm/*.c" "src/shell/*.c" "src/fs/*.c" + "src/syscalls/*.c" "data/*.c" ) diff --git a/kernel/inc/arch/x86_64/syscall.h b/kernel/inc/arch/x86_64/syscall.h index 5fb2b39..92fd997 100644 --- a/kernel/inc/arch/x86_64/syscall.h +++ b/kernel/inc/arch/x86_64/syscall.h @@ -13,8 +13,10 @@ typedef enum { SYS_EXIT, - SYS_EXEC, + SYS_SPAWN, + SYS_MEM, SYS_WRITE, + SYS_READ, } syscalls; void syscall_init(); \ No newline at end of file diff --git a/kernel/inc/core/loader.h b/kernel/inc/core/loader.h index e57b41d..3b46941 100644 --- a/kernel/inc/core/loader.h +++ b/kernel/inc/core/loader.h @@ -4,7 +4,6 @@ #pragma once #include -#include -bool exec_init(process* p, const char* path); +i32 process_spawn(const char* path, const char* name); void init_task_entry(); \ No newline at end of file diff --git a/kernel/inc/core/scheduler.h b/kernel/inc/core/scheduler.h index 5ce4de7..ff7d8d0 100644 --- a/kernel/inc/core/scheduler.h +++ b/kernel/inc/core/scheduler.h @@ -15,6 +15,8 @@ typedef struct process { u64 pml4_phys; struct process* parent; char name[32]; + u64 heap_start; + u64 heap_cur; } process; typedef struct task { @@ -22,6 +24,7 @@ typedef struct task { struct task* next; u32 id; u32 sleep_ticks; + process_state task_state; // reusing process_state cuz wn u64 kernel_stack_top; process* proc; } task; diff --git a/kernel/inc/drivers/console.h b/kernel/inc/drivers/console.h index c02fa9c..11bbd49 100644 --- a/kernel/inc/drivers/console.h +++ b/kernel/inc/drivers/console.h @@ -13,6 +13,7 @@ void console_set_color(u32 color); void console_set_default_color(u32 color); void console_set_cursor_pos(SG_Point *p); char console_getc(); +void console_putc(char c); void kprint(const char *str); void kprintf(const char *fmt, ...); void kgets(char* buff, u32 lim); diff --git a/kernel/inc/syscalls/io.h b/kernel/inc/syscalls/io.h new file mode 100644 index 0000000..5ab5626 --- /dev/null +++ b/kernel/inc/syscalls/io.h @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#pragma once +#include + +u64 sys_write(u64 fd, u64 buff, u64 len); +u64 sys_read(u64 fd, u64 buff, u64 count); \ No newline at end of file diff --git a/kernel/inc/syscalls/mem.h b/kernel/inc/syscalls/mem.h new file mode 100644 index 0000000..6d6d396 --- /dev/null +++ b/kernel/inc/syscalls/mem.h @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#pragma once +#include + +u64 sys_mem(u64 size); \ No newline at end of file diff --git a/kernel/inc/syscalls/proc.h b/kernel/inc/syscalls/proc.h new file mode 100644 index 0000000..e9b51c4 --- /dev/null +++ b/kernel/inc/syscalls/proc.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#pragma once + +#include + +u64 sys_exit(i32 code); +u64 sys_spawn(const char* path); \ No newline at end of file diff --git a/kernel/src/arch/x86_64/cpuinfo.c b/kernel/src/arch/x86_64/cpuinfo.c index 321b92b..5924123 100644 --- a/kernel/src/arch/x86_64/cpuinfo.c +++ b/kernel/src/arch/x86_64/cpuinfo.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later // Copyright (c) 2026 0xKarinyash -#include "types.h" +#include #include #define MSR_GS_BASE 0xC0000101 diff --git a/kernel/src/arch/x86_64/syscall.asm b/kernel/src/arch/x86_64/syscall.asm index e0c622e..78e33d2 100644 --- a/kernel/src/arch/x86_64/syscall.asm +++ b/kernel/src/arch/x86_64/syscall.asm @@ -60,7 +60,6 @@ syscall_entry: pop r11 ; rflags add rsp, 8 ; skip rsp - mov [gs:8], rsp mov rsp, [gs:0] swapgs diff --git a/kernel/src/arch/x86_64/syscall.c b/kernel/src/arch/x86_64/syscall.c index 788a7e1..b6b6f41 100644 --- a/kernel/src/arch/x86_64/syscall.c +++ b/kernel/src/arch/x86_64/syscall.c @@ -8,6 +8,10 @@ #include #include +#include +#include +#include + static inline void wrmsr(u32 msr, u64 val) { u32 low = (u32)val; u32 high = (u32)(val >> 32); @@ -47,20 +51,14 @@ void syscall_init() { } u64 syscall_dispatch(u64 id, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5) { + __asm__ volatile("cli"); switch (id) { - case SYS_EXIT: { - kprintf("\n[Dewar] process exited with code %d", arg1); - while(1) __asm__ ("hlt"); // stub - return 0; - } - case SYS_EXEC: { - kprintf("\n[Dewar] process called exec syscall"); - return 0; // stub as well - } - case SYS_WRITE: { - kprintf("%s", (char*)arg1); - return 0; - } + case SYS_EXIT: return sys_exit(arg1); + case SYS_SPAWN: return sys_spawn((const char*)arg1); + case SYS_MEM: return sys_mem(arg1); + case SYS_WRITE: return sys_write(arg1, arg2, arg3); + case SYS_READ: return sys_read(arg1, arg2, arg3); default: kprintf("[Dewar] Unknown syscall %d\n", id); return -1; } + __asm__ volatile("sti"); } \ No newline at end of file diff --git a/kernel/src/core/hot.c b/kernel/src/core/hot.c index 1b88f20..c1c7b9b 100644 --- a/kernel/src/core/hot.c +++ b/kernel/src/core/hot.c @@ -19,27 +19,30 @@ u64 load_hot(process* proc, u8* data) { } hot_segment* segments = (hot_segment*)(data + sizeof(hot_header)); - u64 kernel_cr3 = vmm_get_current_cr3(); for (u64 i = 0; i < header->segments_count; i++) { hot_segment* seg = &segments[i]; if (seg->memsz == 0) continue; - u64 start = seg->vaddr & ~(0xFFF); - u64 end = (seg->vaddr + seg->memsz + 0xFFF) & ~(0xFFF); + u64 start = seg->vaddr & ~(0xFFFULL); + u64 end = (seg->vaddr + seg->memsz + 0xFFF) & ~(0xFFFULL); + for (u64 addr = start; addr < end; addr += PAGE_SIZE) { void* phys = pmm_alloc_page(); - vmm_map_page((u64*)proc->pml4_phys, (u64)phys, addr, PTE_USER | PTE_RW | PTE_PRESENT); - } + vmm_map_page((u64*)proc->pml4_phys, (u64)phys, addr, PTE_USER | PTE_RW | PTE_PRESENT); + void* kernel_virt = (void*)((u64)phys + HHDM_OFFSET); + memset(kernel_virt, 0, PAGE_SIZE); + u64 page_overleap_start = (addr > seg->vaddr) ? addr : seg->vaddr; + u64 page_overleap_end = (addr + PAGE_SIZE < seg->vaddr + seg->filesz) + ? (addr + PAGE_SIZE) + : (seg->vaddr + seg->filesz); + if (page_overleap_start < page_overleap_end) { + u64 copy_size = page_overleap_end - page_overleap_start; + u64 src_offset = seg->offset + (page_overleap_start - seg->vaddr); + u64 dst_offset = page_overleap_start - addr; - load_cr3(proc->pml4_phys); - if (seg->filesz > 0) memcpy((void*)seg->vaddr, data + seg->offset, seg->filesz); - if (seg->memsz > seg->filesz) { - u64 bss_start = seg->vaddr + seg->filesz; - u64 bss_len = seg->memsz - seg->filesz; - memset((void*)bss_start, 0, bss_len); + memcpy((u8*)kernel_virt + dst_offset, data + src_offset, copy_size); + } } - - load_cr3(kernel_cr3); } return header->entry_point; diff --git a/kernel/src/core/loader.c b/kernel/src/core/loader.c index 0c6c867..4db4014 100644 --- a/kernel/src/core/loader.c +++ b/kernel/src/core/loader.c @@ -20,28 +20,48 @@ #include extern task* curr_task; +extern u32 next_pid; #define USER_STACK_TOP 0x70000000 +#define HEAP_START 0x40000000 -void init_task_entry() { - process* init_proc = (process*)malloc(sizeof(process)); - init_proc->pid = 1; - init_proc->state = RUNNING; - init_proc->pml4_phys = vmm_create_address_space(); - strcpy(init_proc->name, "init"); +i32 process_spawn(const char* path, const char* name) { + fs_node* file = vfs_open(path); + if (!file) return -1; - fs_node* file = vfs_open("/init"); - if (!file) panic("FATAL: /init not found!"); + process* new_proc = (process*)malloc(sizeof(process)); + if (!new_proc) return -2; + memset(new_proc, 0, sizeof(process)); + new_proc->pid = next_pid++; + new_proc->state = RUNNING; + new_proc->pml4_phys = vmm_create_address_space(); + new_proc->heap_start = HEAP_START; + new_proc->heap_cur = HEAP_START; + strncpy(new_proc->name, name, 31); u8* file_buffer = (u8*)malloc(file->len); + if (!file_buffer) { + free(new_proc); + return -3; + } vfs_read(file, 0, file->len, file_buffer); - u64 entry = load_hot(init_proc, file_buffer); - if (!entry) panic("Invalid HOT executable"); - - free(file_buffer); - vmm_setup_user_stack((u64*)init_proc->pml4_phys); - sched_spawn((void(*)())entry, init_proc, true, USER_STACK_TOP); + u64 entry = load_hot(new_proc, file_buffer); + if (!entry) return -4; - while(1) { __asm__("sti; hlt"); } + free(file_buffer); + + vmm_setup_user_stack((u64*)new_proc->pml4_phys); + sched_spawn((void(*)())entry, new_proc, true, USER_STACK_TOP); + + return new_proc->pid; +} + +void init_task_entry() { + i32 pid = process_spawn("/init", "init"); + if (pid < 0) { + panic("FATAL: Failed to spawn /init"); + } + + while (1) { __asm__("sti; hlt"); } } \ No newline at end of file diff --git a/kernel/src/core/panic.c b/kernel/src/core/panic.c index eda398e..645bb85 100644 --- a/kernel/src/core/panic.c +++ b/kernel/src/core/panic.c @@ -32,7 +32,6 @@ const char* fun_messages[] = { "Code have been eaten by Aliens", "That's all, folks!", "Raiden, answer me, Raiden, respond! Raiden?! RAIDEEEEEEEEEN!", - "Fatal error has been occurred.\n\t\t\t\t Your device will be terminated in 30 seconds.", "I'll be back", "Hastla la vista, baby", "Ti chego mne tut nagovoril...", diff --git a/kernel/src/core/scheduler.c b/kernel/src/core/scheduler.c index a5f783a..b6ae56f 100644 --- a/kernel/src/core/scheduler.c +++ b/kernel/src/core/scheduler.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,12 @@ extern u64 pml4_kernel_phys; static process kernel_process; +void idle_task() { + while (1) { + __asm__ volatile ("hlt"); + } +} + void sched_init() { kernel_process.pid = 0; kernel_process.state = RUNNING; @@ -24,12 +31,15 @@ void sched_init() { strcpy(kernel_process.name, "kernel"); task* kt = (task*)malloc(sizeof(task)); + memset(kt, 0, sizeof(task)); kt->id = 0; kt->proc = &kernel_process; kt->sleep_ticks = 0; kt->next = kt; + kt->task_state = RUNNING; curr_task = kt; + sched_spawn(idle_task, &kernel_process, false, 0); } task* sched_spawn(void(*entry)(), process* owner, bool is_user, u64 fixed_user_stack) { @@ -66,6 +76,7 @@ task* sched_spawn(void(*entry)(), process* owner, bool is_user, u64 fixed_user_s t->sleep_ticks = 0; t->next = curr_task->next; t->kernel_stack_top = (u64)stack_base + stack_size; + t->task_state = RUNNING; curr_task->next = t; return t; } @@ -84,6 +95,12 @@ u64 sched_next(u64 curr_rsp) { if (curr_task->sleep_ticks > 0) curr_task->sleep_ticks--; task* next = curr_task->next; + while (next->task_state == DEAD) { + // TODO: add gc here; + next = next->next; + if (next == curr_task) panic("no alive tasks"); + } + while (next != curr_task && next->sleep_ticks > 0) next = next->next; // what the fuck i just wrote if (next->proc->pml4_phys != curr_task->proc->pml4_phys) load_cr3(next->proc->pml4_phys); diff --git a/kernel/src/drivers/console.c b/kernel/src/drivers/console.c index d11b9ab..403c706 100644 --- a/kernel/src/drivers/console.c +++ b/kernel/src/drivers/console.c @@ -80,7 +80,7 @@ void console_set_cursor_pos(SG_Point *p) { s_cursor_pos.y = p->y; } -static void console_putc(char c) { +void console_putc(char c) { serial_writec(c); if (!ctx_ptr) return; if (c == '\n') { diff --git a/kernel/src/syscalls/io.c b/kernel/src/syscalls/io.c new file mode 100644 index 0000000..cf04278 --- /dev/null +++ b/kernel/src/syscalls/io.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#include +#include + +u64 sys_write(u64 fd, u64 buff, u64 len) { + if (fd == 1 || fd == 2) { + char* str = (char*)buff; + for (u64 i = 0; i < len; i++) { + console_putc(str[i]); + } + return len; + } + return 0; +} + +u64 sys_read(u64 fd, u64 buff, u64 count) { + char* buf = (char*)buff; + if (fd == 0) { + for (u64 i = 0; i < count; i++) { + buf[i] = console_getc(); + } + return count; + } + return 0; +} \ No newline at end of file diff --git a/kernel/src/syscalls/mem.c b/kernel/src/syscalls/mem.c new file mode 100644 index 0000000..86b9d92 --- /dev/null +++ b/kernel/src/syscalls/mem.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#include + +#include + +#include +#include +#include + +extern task* curr_task; + +u64 sys_mem(u64 size) { + if (size == 0) return 0; + process* proc = curr_task->proc; + u64 addr_to_ret = proc->heap_cur; + + u64 pages_needed = (size + (PAGE_SIZE-1)) / PAGE_SIZE; + + for (u64 i = 0; i < pages_needed; i++) { + void* phys = pmm_alloc_page(); + if (!phys) return 0; + + vmm_map_page((u64*)proc->pml4_phys, (u64)phys, proc->heap_cur, PTE_PRESENT | PTE_RW | PTE_USER); + memset((void*)PHYS_TO_HHDM((u64)phys), 0, PAGE_SIZE); + + proc->heap_cur += 4096; + } + + return addr_to_ret; +} diff --git a/kernel/src/syscalls/proc.c b/kernel/src/syscalls/proc.c new file mode 100644 index 0000000..013f2d4 --- /dev/null +++ b/kernel/src/syscalls/proc.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#include +#include +#include +#include + +extern task* curr_task; + +u64 sys_exit(i32 code) { + kprintf("\n[Dewar] process %s exited with code %d", curr_task->proc->name, code); + curr_task->task_state = DEAD; + sched_next(curr_task->rsp); + while (1) { __asm__ ("hlt"); } +} + + +u64 sys_spawn(const char* path) { + return process_spawn(path, path); +} + diff --git a/userspace/CMakeLists.txt b/userspace/CMakeLists.txt index 2d9caf3..18c1f0d 100644 --- a/userspace/CMakeLists.txt +++ b/userspace/CMakeLists.txt @@ -7,4 +7,6 @@ set(CMAKE_C_EXTENSIONS OFF) message(STATUS "Building termOS's userspace") -add_subdirectory(init) \ No newline at end of file +add_subdirectory(libterm) +add_subdirectory(init) +add_subdirectory(debug) \ No newline at end of file diff --git a/userspace/debug/CMakeLists.txt b/userspace/debug/CMakeLists.txt new file mode 100644 index 0000000..fc72855 --- /dev/null +++ b/userspace/debug/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.20) +project(termOSdbg LANGUAGES C) + +file(GLOB_RECURSE debug_SOURCES "src/*.c") + +add_termos_executable(debug "${debug_SOURCES}") diff --git a/userspace/debug/src/main.c b/userspace/debug/src/main.c new file mode 100644 index 0000000..db5df75 --- /dev/null +++ b/userspace/debug/src/main.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#include +#include + +#define VECTOR_BUFFER_SIZE 8 + +typedef struct { + i32* data; + u64 size; + u64 capacity; +} Vector; + +void vector_init(Vector* vec) { + vec->data = nullptr; + vec->size = 0; + vec->capacity = 0; +} + +u8 vector_append(Vector* vec, const i32 num) { + if (vec->size >= vec->capacity) { + u64 new_cap = (vec->capacity == 0) ? VECTOR_BUFFER_SIZE : vec->capacity * 2; + + i32* new_data = realloc(vec->data, new_cap * sizeof(i32)); + if (!new_data) return 2; // Out of memory + + vec->data = new_data; + vec->capacity = new_cap; + } + vec->data[vec->size] = num; + vec->size++; + + return 0; +} + +void vector_reset(Vector* vec) { + vec->size = 0; +} + +u8 vector_reserve(Vector* vec, u64 new_cap) { + if (new_cap <= vec->capacity) return 0; + i32* new_data = realloc(vec->data, new_cap * sizeof(i32)); + if (!new_data) return 2; + + vec->data = new_data; + vec->capacity = new_cap; + + return 0; +} + +void vector_free(Vector* vec) { + free(vec->data); + vec->data = nullptr; + vec->size = vec->capacity = 0; +} + +void render_list(Vector* numbers) { + printf("\nNumbers [%d/%d]: [", numbers->size, numbers->capacity); + if (numbers->size == 0) printf("]"); + for (u64 i = 0; i < numbers->size; i++) { + printf("%d", numbers->data[i]); + if (i + 1 >= numbers->size) { + printf("]"); + break; + } + printf(", "); + } +} + +int main() { + Vector nums; + vector_init(&nums); + printf("Heap test\n"); + printf("press any key to add 1 number press 'q' to exit\n"); + char c = 'd'; + u64 i = 0; + while (c != 'q') { + render_list(&nums); + vector_append(&nums, i); + i++; + c = getchar(); + } + vector_free(&nums); +} \ No newline at end of file diff --git a/userspace/init/CMakeLists.txt b/userspace/init/CMakeLists.txt index 8c4b181..e22ea24 100644 --- a/userspace/init/CMakeLists.txt +++ b/userspace/init/CMakeLists.txt @@ -1,33 +1,6 @@ cmake_minimum_required(VERSION 3.20) project(termOSinit LANGUAGES C) -set(CMAKE_C_STANDARD 23) -set(CMAKE_C_STANDARD_REQUIRED ON) -set(CMAKE_C_EXTENSIONS OFF) +file(GLOB_RECURSE INIT_SOURCES "src/*.c") -get_filename_component(TOOLS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../tools/elf2hot" ABSOLUTE) -set(FINAL_INIT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../initramfs/init") - -file(GLOB_RECURSE INIT_SOURCES "src/*.asm" "src/*.c") - -add_executable(init_elf ${INIT_SOURCES}) - -set_target_properties(init_elf PROPERTIES - OUTPUT_NAME "init" - LINKER_LANGUAGE C -) - -target_link_options(init_elf PRIVATE - -nostdlib - -static - -T "${CMAKE_CURRENT_SOURCE_DIR}/linker.ld" -) - -add_custom_command(TARGET init_elf POST_BUILD - COMMAND cargo run --release --quiet -- $ ${FINAL_INIT_PATH} - - WORKING_DIRECTORY ${TOOLS_DIR} - - COMMENT "Cargo is converting ELF to HOT! format..." - VERBATIM -) \ No newline at end of file +add_termos_executable(init "${INIT_SOURCES}") diff --git a/userspace/init/src/crt0.asm b/userspace/init/src/crt0.asm deleted file mode 100644 index a39c6a7..0000000 --- a/userspace/init/src/crt0.asm +++ /dev/null @@ -1,10 +0,0 @@ -[bits 64] - -section .text -global start -extern main - -start: - call main -.hang: - jmp .hang \ No newline at end of file diff --git a/userspace/init/src/main.c b/userspace/init/src/main.c index f4bb7e8..56cb533 100644 --- a/userspace/init/src/main.c +++ b/userspace/init/src/main.c @@ -1,16 +1,12 @@ -static inline void sys_write(const char* str) { - __asm__ volatile ( - "mov $2, %%rax\n\t" - "mov %0, %%rdi\n\t" - "syscall" - : - : "r"(str) - : "rax", "rdi", "rcx", "r11", "memory" - ); -} +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#include +#include +#include int main() { - sys_write("Meowww!!!\n"); - sys_write("Nyaaaaa"); - for (int i = 0; i < 100; i++) sys_write("a"); + printf("Launching debug\n"); + spawn("debug"); + while (1) {} } \ No newline at end of file diff --git a/userspace/libterm/CMakeLists.txt b/userspace/libterm/CMakeLists.txt new file mode 100644 index 0000000..0ff95e6 --- /dev/null +++ b/userspace/libterm/CMakeLists.txt @@ -0,0 +1,63 @@ +cmake_minimum_required(VERSION 3.16) +project(libterm LANGUAGES C ASM_NASM) + +set(USER_C_FLAGS + -ffreestanding + -nostdlib + -nostdinc + -fno-stack-protector + -fno-pic + -fno-pie + -m64 + -mno-red-zone + -mcmodel=small + -Wall + -Wextra + -Werror + -O2 +) + +set(CMAKE_ASM_NASM_FLAGS "-f elf64") + +set(LIBTERM_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/src/syscalls.asm + ${CMAKE_CURRENT_SOURCE_DIR}/src/stdio.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/malloc.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/string.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ctype.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/process.c +) + +add_library(term STATIC ${LIBTERM_SOURCES}) + +target_compile_options(term PRIVATE $<$:${USER_C_FLAGS}>) + +target_include_directories(term PUBLIC inc) + +add_library(crt0_obj OBJECT src/crt0.asm) +target_compile_options(crt0_obj PRIVATE $<$:${USER_C_FLAGS}>) + +function(add_termos_executable NAME SOURCES) + add_executable(${NAME} ${SOURCES}) + + target_sources(${NAME} PRIVATE $) + + target_compile_options(${NAME} PRIVATE $<$:${USER_C_FLAGS}>) + target_link_libraries(${NAME} PRIVATE term) + + target_link_options(${NAME} PRIVATE + -T ${libterm_SOURCE_DIR}/linker.ld + -nostdlib + -static + ) + + set(ELF2HOT_DIR "${CMAKE_SOURCE_DIR}/tools/elf2hot") + set(FINAL_HOT_PATH "${CMAKE_SOURCE_DIR}/initramfs/${NAME}") + + add_custom_command(TARGET ${NAME} POST_BUILD + COMMAND cargo run --release --quiet -- $ ${FINAL_HOT_PATH} + WORKING_DIRECTORY ${ELF2HOT_DIR} + COMMENT "elf2hot: Converting ${NAME} to HOT! format..." + VERBATIM + ) +endfunction() \ No newline at end of file diff --git a/userspace/libterm/inc/malloc.h b/userspace/libterm/inc/malloc.h new file mode 100644 index 0000000..ac79909 --- /dev/null +++ b/userspace/libterm/inc/malloc.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#pragma once +#include + +#define HEADER_MAGIC 0x1CE1CE + +typedef struct block_header { + u64 magic; + struct block_header* next; + struct block_header* prev; + u64 size; + bool is_free; +} block_header; + +void* malloc(u64 size); +void free(void* ptr); +void* realloc(void* ptr, u64 new_size); \ No newline at end of file diff --git a/userspace/libterm/inc/process.h b/userspace/libterm/inc/process.h new file mode 100644 index 0000000..59778f4 --- /dev/null +++ b/userspace/libterm/inc/process.h @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#pragma once +#include + +u64 spawn(const char* path); \ No newline at end of file diff --git a/userspace/libterm/inc/stdarg.h b/userspace/libterm/inc/stdarg.h new file mode 100644 index 0000000..099c20a --- /dev/null +++ b/userspace/libterm/inc/stdarg.h @@ -0,0 +1,8 @@ +#pragma once + +typedef __builtin_va_list va_list; + +#define va_start(v, l) __builtin_va_start(v, l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v, l) __builtin_va_arg(v, l) +#define va_copy(d, s) __builtin_va_copy(d, s) diff --git a/userspace/libterm/inc/stdio.h b/userspace/libterm/inc/stdio.h new file mode 100644 index 0000000..e682313 --- /dev/null +++ b/userspace/libterm/inc/stdio.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#pragma once +#include + +void printf(const char *fmt, ...); +int getchar(); +char* gets(char* str); +char* gets_s(char* str, u64 size); \ No newline at end of file diff --git a/userspace/libterm/inc/string.h b/userspace/libterm/inc/string.h new file mode 100644 index 0000000..0344051 --- /dev/null +++ b/userspace/libterm/inc/string.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#pragma once +#include + +void *memset(void *ptr, int value, usize num); +void* memcpy(void* dest, const void* src, u64 n); +i32 strcmp(const char *s1, const char *s2); +i32 strncmp(const char* s1, const char* s2, u64 n); +char* strcpy(char* dest, const char* src); +char* strncpy(char* dest, const char* src, u64 n); +u64 strlen(const char* str); \ No newline at end of file diff --git a/userspace/libterm/inc/types.h b/userspace/libterm/inc/types.h new file mode 100644 index 0000000..422ef65 --- /dev/null +++ b/userspace/libterm/inc/types.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2025 0xKarinyash + +#pragma once + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef signed char i8; +typedef signed short i16; +typedef signed int i32; +typedef signed long long i64; + +typedef u64 usize; +typedef u64 uintptr_t; + +#ifndef __cplusplus + #define bool _Bool + #define true 1 + #define false 0 +#endif diff --git a/userspace/init/linker.ld b/userspace/libterm/linker.ld similarity index 96% rename from userspace/init/linker.ld rename to userspace/libterm/linker.ld index f688562..718aab9 100644 --- a/userspace/init/linker.ld +++ b/userspace/libterm/linker.ld @@ -1,4 +1,4 @@ -ENTRY(start) +ENTRY(_start) SECTIONS { diff --git a/userspace/libterm/src/crt0.asm b/userspace/libterm/src/crt0.asm new file mode 100644 index 0000000..bed6c26 --- /dev/null +++ b/userspace/libterm/src/crt0.asm @@ -0,0 +1,14 @@ +; SPDX-License-Identifier: GPL-3.0-or-later +; Copyright (c) 2026 0xKarinyash + +[bits 64] + +section .text +global _start +extern main +extern sys_exit + +_start: + call main + mov rdi, rax + call sys_exit \ No newline at end of file diff --git a/userspace/libterm/src/ctype.c b/userspace/libterm/src/ctype.c new file mode 100644 index 0000000..e69de29 diff --git a/userspace/libterm/src/malloc.c b/userspace/libterm/src/malloc.c new file mode 100644 index 0000000..546421d --- /dev/null +++ b/userspace/libterm/src/malloc.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash +// brutally copypasting from kernel/mm/heap.c + +#include +#include + +static block_header* heap_list_head = nullptr; +extern u64 sys_mem(u64 size); + +void combine_forward(block_header* curr) { + if (!curr->next || !curr->next->is_free) return; + curr->size += sizeof(block_header) + curr->next->size; + curr->next = curr->next->next; + if (curr->next) curr->next->prev = curr; // what the fuck +} + +void* malloc(u64 size) { + if (size == 0) return nullptr; + u64 aligned_size = (size + 15) & ~15; + + block_header* curr = heap_list_head; + block_header* last = nullptr; + while (curr) { + if (curr->is_free && curr->size >= aligned_size) { + if (curr->size > aligned_size + sizeof(block_header) + 16) { + block_header* new_block = (block_header*)((u64)curr + sizeof(block_header) + aligned_size); + new_block->size = curr->size - aligned_size - sizeof(block_header); + new_block->is_free = true; + new_block->next = curr->next; + new_block->prev = curr; + new_block->magic = HEADER_MAGIC; + + if (curr->next) curr->next->prev = new_block; + curr->next = new_block; + curr->size = aligned_size; + } + curr->is_free = false; + return (void*)((u64)curr + sizeof(block_header)); + } + last = curr; + curr = curr->next; + } + + u64 need_to_alloc = aligned_size + sizeof(block_header); + + u64 page_aligned_size = (need_to_alloc + 4095) & ~4095; + + u64 new_mem_addr = sys_mem(page_aligned_size); + if (new_mem_addr == 0) return nullptr; + + block_header* new_block = (block_header*)new_mem_addr; + new_block->size = page_aligned_size - sizeof(block_header); + new_block->is_free = true; + new_block->magic = HEADER_MAGIC; + new_block->next = nullptr; + new_block->prev = last; + + if (last) { + last->next = new_block; + } else { + heap_list_head = new_block; + } + + return malloc(size); +} + +void free(void* ptr) { + if (!ptr) return; + + block_header* curr = (block_header*)((u64)ptr - sizeof(block_header)); + if (curr->magic != HEADER_MAGIC) return; + + curr->is_free = true; + if (curr->next && curr->next->is_free) combine_forward(curr); + if (curr->prev && curr->prev->is_free) combine_forward(curr->prev); +} + +void* realloc(void* ptr, u64 new_size) { + if (!ptr) return malloc(new_size); + if (new_size == 0) { + free(ptr); + return nullptr; + } + + block_header* curr = (block_header*)((u64)ptr - sizeof(block_header)); + if (curr->size >= new_size) return ptr; + + if (curr->next && + curr->next->is_free && + (curr->size + sizeof(block_header) + curr->next->size) >= new_size) { // why ts so fucking unreadable + combine_forward(curr); + return ptr; + } + + void* new_ptr = malloc(new_size); + if (!new_ptr) return nullptr; + + memcpy(new_ptr, ptr, curr->size); + free(ptr); + + return new_ptr; +} \ No newline at end of file diff --git a/userspace/libterm/src/process.c b/userspace/libterm/src/process.c new file mode 100644 index 0000000..95a5107 --- /dev/null +++ b/userspace/libterm/src/process.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#include + +extern u64 sys_spawn(const char* path); + +u64 spawn(const char* path) { + return sys_spawn(path); +} \ No newline at end of file diff --git a/userspace/libterm/src/stdio.c b/userspace/libterm/src/stdio.c new file mode 100644 index 0000000..9611681 --- /dev/null +++ b/userspace/libterm/src/stdio.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#include +#include +#include +#include + +#define EOF (-1) + +extern u64 sys_read(u64 fd, void* buf, u64 len); +extern u64 sys_write(u64 fd, const void* buf, u64 len); + +static void putchar(char c) { + sys_write(1, &c, 1); +} + +static void print_str(const char* str) { + if (!str) str = "(null)"; + sys_write(1, str, strlen(str)); +} + +static void print_dec(long long n) { + if (n < 0) { + putchar('-'); + n = -n; + } + unsigned long long u = (unsigned long long)n; + char buffer[32]; + int i = 0; + do { + buffer[i++] = (u % 10) + '0'; + u /= 10; + } while (u > 0); + while (--i >= 0) putchar(buffer[i]); +} + +static void print_hex(unsigned long long u, int padding) { + char buffer[16]; + int i = 0; + const char* hex_chars = "0123456789ABCDEF"; + + do { + buffer[i++] = hex_chars[u % 16]; + u /= 16; + } while (u > 0); + + while (i < padding) buffer[i++] = '0'; + + print_str("0x"); + while (--i >= 0) putchar(buffer[i]); +} + +void printf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + + for (int i = 0; fmt[i] != '\0'; i++) { + if (fmt[i] == '%') { + i++; + switch (fmt[i]) { + case 's': print_str(va_arg(args, const char*)); break; + case 'c': putchar((char)va_arg(args, int)); break; + case 'd': print_dec(va_arg(args, int)); break; + case 'x': print_hex(va_arg(args, unsigned long long), 0); break; + case 'X': print_hex(va_arg(args, unsigned long long), 16); break; + case '%': putchar('%'); break; + default: putchar(fmt[i]); break; + } + } + else { + putchar(fmt[i]); + } + } + + va_end(args); +} + +int getchar() { + char c; + unsigned long long res = sys_read(0, &c, 1); + if (res <= 0) return EOF; + return (int)(unsigned char)c; +} + +char* gets(char* str) { + int i = 0; + int c; + + while (1) { + c = getchar(); + + if (c == EOF || c == '\n' || c == '\r') { + break; + } + + str[i++] = (char)c; + + char ch = (char)c; + sys_write(1, &ch, 1); + } + + str[i] = '\0'; + + char nl = '\n'; + sys_write(1, &nl, 1); + + return str; +} + +char* gets_s(char* str, u64 size) { + if (size == 0) return str; + + u64 i = 0; + int c; + + while (i < size - 1) { + c = getchar(); + if (c == EOF || c == '\n' || c == '\r') break; + + str[i++] = (char)c; + + char ch = (char)c; + sys_write(1, &ch, 1); + } + + str[i] = '\0'; + char nl = '\n'; + sys_write(1, &nl, 1); + + return str; +} \ No newline at end of file diff --git a/userspace/libterm/src/string.c b/userspace/libterm/src/string.c new file mode 100644 index 0000000..ac5f7e1 --- /dev/null +++ b/userspace/libterm/src/string.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 0xKarinyash + +#include + +void *memset(void *ptr, int value, usize num) { + u8 *p = (u8 *)ptr; + while (num--) { + *p++ = (u8)value; + } + return ptr; +} + +void* memcpy(void* dest, const void* src, u64 n) { + u8* d = (u8*)dest; + const u8* s = (const u8*)src; + + while (n >= 8) { + *(u64*)d = *(const u64*)s; + d += 8; + s += 8; + n -= 8; + } + + while (n > 0) { + *d++ = *s++; + n--; + } + + return dest; +} + +i32 strcmp(const char *s1, const char *s2) { + while (*s1 && (*s1 == *s2)) { + s1++; + s2++; + } + return *(const unsigned char*)s1 - *(const unsigned char*)s2; +} + +i32 strncmp(const char* s1, const char* s2, u64 n) { + while (n > 0) { + if (*s1 != *s2) return *(unsigned char*)s1 - *(unsigned char*)s2; + if (*s1 == '\0') return 0; + s1++; + s2++; + n--; + } + + return 0; +} + +char* strcpy(char* dest, const char* src) { + char* saved = dest; + while (*src) *dest++ = *src++; + *dest = 0; + return saved; +} + +char* strncpy(char* dest, const char* src, u64 n) { + char* saved = dest; + while (*src && n > 0) { + *dest++ = *src++; + n--; + } + while (n > 0) { + *dest++ = 0; + n--; + } + return saved; +} + +u64 strlen(const char* str) { + u64 res = 0; + for (res = 0; str[res]; res++); + return res; +} \ No newline at end of file diff --git a/userspace/libterm/src/syscalls.asm b/userspace/libterm/src/syscalls.asm new file mode 100644 index 0000000..122a6b9 --- /dev/null +++ b/userspace/libterm/src/syscalls.asm @@ -0,0 +1,37 @@ +; SPDX-License-Identifier: GPL-3.0-or-later +; Copyright (c) 2026 0xKarinyash + +bits 64 + +section .text + +global sys_exit +global sys_spawn +global sys_mem +global sys_write +global sys_read + +sys_exit: + mov rax, 0 + syscall + ret + +sys_spawn: + mov rax, 1 + syscall + ret + +sys_mem: + mov rax, 2 + syscall + ret + +sys_write: + mov rax, 3 + syscall + ret + +sys_read: + mov rax, 4 + syscall + ret