feat(userspace): userspace support

build: updated CMakeLists to build and copy to initramfs

wip(syscalls): initial syscalls

chore(ksh/kfetch): minor changes

chore(gitignore): added initramfs/*
This commit is contained in:
Karina
2026-01-29 21:25:45 +04:00
parent 3f3a026432
commit 519e1a38e6
23 changed files with 399 additions and 135 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
.cache
build*
.venv
initrd/image.cpio
initrd/image.cpio
initramfs/*
+2 -1
View File
@@ -28,6 +28,7 @@ add_compile_definitions(TERMOS_VERSION=\"${KERNEL_VERSION}\")
add_subdirectory(bootloader)
add_subdirectory(kernel)
add_subdirectory(userspace)
if(MCOPY_EXE AND MKFS_EXE AND CPIO_EXE)
set(IMG_FILE "${CMAKE_BINARY_DIR}/termOS.img")
@@ -41,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}
DEPENDS ${INIT_FILES} init
VERBATIM
COMMENT "Packing initramfs to cpio..."
)
-1
View File
@@ -1 +0,0 @@
Meow!!
-1
View File
@@ -1 +0,0 @@
Nyaaa
+7 -3
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#pragma once
#include <types.h>
#define CPU_FEAT_FPU (1ULL << 0)
@@ -24,7 +24,11 @@
#define CPU_FEAT_HYPERVISOR (1ULL << 30)
typedef struct {
i32 features;
u64 user_rsp;
u64 kernel_rsp;
u64 self;
u64 features;
char vendor[13];
u32 family;
u32 model;
@@ -32,5 +36,5 @@ typedef struct {
extern cpu_info g_cpu;
void cpuinfo_init();
void cpuinfo_init(u64 kernel_stack_top);
bool cpu_has(u64 feature);
+20
View File
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#pragma once
#define MSR_EFER 0xC0000080
#define MSR_STAR 0xC0000081
#define MSR_LSTAR 0xC0000082
#define MSR_FMASK 0xC0000084
#define MSR_KERNEL_GS_BASE 0xC0000102
#define EFER_SCE 0x01 // System Call Enable
typedef enum {
SYS_EXIT,
SYS_EXEC,
SYS_WRITE,
} syscalls;
void syscall_init();
+9
View File
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#pragma once
#include <types.h>
bool exec_init(const char* path);
void init_task_entry();
+6
View File
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#pragma once
void jump_to_userspace(void* entry, void* user_stack_top);
+1 -9
View File
@@ -26,16 +26,8 @@ typedef struct {
const unsigned char* base;
} SG_Font;
typedef struct {
SG_Context *ctx;
SG_Point pos;
const char *title;
} SG_Window;
void sg_init(SG_Context *ctx);
u32 sg_get_pixel(SG_Context *ctx, SG_Point *p);
void sg_put_img(SG_Context *ctx, SG_Point *p, SG_Image *img);
void sg_draw_rect(SG_Context *ctx, SG_Point *p, u32 w, u32 h, u32 color);
void sg_draw_char_bitmap(SG_Context *ctx, SG_Point *p, char c, u32 color, SG_Font *font);
SG_Window* create_window(const char *title, SG_Point* size, SG_Point* position);
void composer_task();
void sg_draw_char_bitmap(SG_Context *ctx, SG_Point *p, char c, u32 color, SG_Font *font);
+15 -1
View File
@@ -4,6 +4,9 @@
#include "types.h"
#include <cpuinfo.h>
#define MSR_GS_BASE 0xC0000101
#define MSR_KERNEL_GS_BASE 0xC0000102
cpu_info g_cpu = {0};
static inline void cpuid(u32 leaf, u32 subleaf, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) {
@@ -13,7 +16,18 @@ static inline void cpuid(u32 leaf, u32 subleaf, u32 *eax, u32 *ebx, u32 *ecx, u3
);
}
void cpuinfo_init() {
static inline void wrmsr(u32 msr, u64 val) {
u32 low = (u32)val;
u32 high = (u32)(val >> 32);
__asm__ volatile("wrmsr" :: "a"(low), "d"(high), "c"(msr));
}
void cpuinfo_init(u64 kernel_stack_top) {
g_cpu.kernel_rsp = kernel_stack_top;
g_cpu.self = (u64)&g_cpu;
wrmsr(MSR_KERNEL_GS_BASE, (u64)&g_cpu);
u32 eax, ebx, ecx, edx;
cpuid(0, 0, &eax, &ebx, &ecx, &edx);
+1
View File
@@ -27,6 +27,7 @@ stack_guard:
global stack_bottom
stack_bottom:
resb 16384
global stack_top
stack_top:
section .text.entry
+70
View File
@@ -0,0 +1,70 @@
; SPDX-License-Identifier: GPL-3.0-or-later
; Copyright (c) 2026 0xKarinyash
bits 64
extern syscall_dispatch
global syscall_entry
section .text
syscall_entry:
swapgs
mov [gs:0], rsp ; saving users rsp in g_cpu.user_rsp
mov rsp, [gs:8] ; load kernel rsp from g_cpu.kernel_rsp
push qword [gs:0]
push r11 ; rflags
push rcx ; rip
push rbp
push rbx
push rdi
push rsi
push rdx
push r10
push r8
push r9
push r12
push r13
push r14
push r15
; preparing c code
; c waits rdi, rsi, rdx, rcx, r8, r9
; user sent rax, rdi, rsi, rdx, r10, r8 (rax=id)
mov r9, r8 ; 6th arg
mov r8, r10 ; 5th arg
mov rcx, rdx ; 4th arg
mov rdx, rsi ; 3rd arg
mov rsi, rdi ; 2nd arg
mov rdi, rax ; 1st arg (id)
call syscall_dispatch
; restoring context except RAX (result there)
pop r15
pop r14
pop r13
pop r12
pop r9
pop r8
pop r10
pop rdx
pop rsi
pop rdi
pop rbx
pop rbp
pop rcx ; rip
pop r11 ; rflags
add rsp, 8 ; skip rsp
mov [gs:8], rsp
mov rsp, [gs:0]
swapgs
o64 sysret
+66
View File
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#include <cpuinfo.h>
#include <syscall.h>
#include <types.h>
#include <drivers/console.h>
#include <mm/pmm.h>
#include <mm/vmm.h>
static inline void wrmsr(u32 msr, u64 val) {
u32 low = (u32)val;
u32 high = (u32)(val >> 32);
__asm__ volatile("wrmsr" :: "a"(low), "d"(high), "c"(msr));
}
static inline u64 rdmsr(u32 msr) {
u32 low, high;
__asm__ volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr));
return ((u64)high << 32) | low;
}
extern void syscall_entry();
void syscall_init() {
u64 efer = rdmsr(MSR_EFER);
wrmsr(MSR_EFER, efer | 1); // Enabling SCE in EFER. Just enabling 0 bit
// setting up STAR
// 32:47 kernel selector (0x08)
// 48:64 user selector (0x01 cuz sysret adds +16 for CS and +8 for SS)
u64 star = (0x13ULL << 48) | (0x08ULL << 32);
wrmsr(MSR_STAR, star);
wrmsr(MSR_LSTAR, (u64)syscall_entry); // setting handler adress
// masking flags (IA32_FMASK)
// 9 bit for finterrupts in syscall (IF) and few more necessary flags
wrmsr(MSR_FMASK, 0x200); // masking only IF
if (g_cpu.kernel_rsp == 0) {
void* stack_phys = pmm_alloc_page();
g_cpu.kernel_rsp = (u64)stack_phys + HHDM_OFFSET + 4096;
}
wrmsr(MSR_KERNEL_GS_BASE, (u64)&g_cpu);
}
u64 syscall_dispatch(u64 id, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5) {
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;
}
default: kprintf("[Dewar] Unknown syscall %d\n", id); return -1;
}
}
+82
View File
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#include <core/loader.h>
#include <core/userspace.h>
#include <core/scheduler.h>
#include <shell/ksh.h>
#include <mm/pmm.h>
#include <mm/vmm.h>
#include <mm/memory.h>
#include <fs/vfs.h>
#include <drivers/console.h>
#include <types.h>
extern u64* pml4_kernel;
bool exec_init(const char* path) {
kprintf("[Loader] loading %s...\n", path);
fs_node* file = vfs_open(path);
if (!file) {
kprintf("[Loader] Error: %s not found in initramfs!\n", path);
return false;
}
u64 virt_code = 0x400000;
u64 virt_stack = 0x800000;
u64 stack_size = 8192;
u64 bytes_left = file->len;
u64 offset = 0;
u64 page_idx = 0;
while (bytes_left > 0) {
void* phys = pmm_alloc_page();
if (!phys) {
kprintf("Loader: OOM!\n");
return false;
}
vmm_map_page(pml4_kernel, (u64)phys, virt_code + (page_idx * 4096), PTE_PRESENT | PTE_RW | PTE_USER);
void* k_ptr = (void*)(HHDM_OFFSET + (u64)phys);
memset(k_ptr, 0, 4096);
u64 chunk = (bytes_left > 4096) ? 4096 : bytes_left;
vfs_read(file, offset, chunk, (u8*)k_ptr);
bytes_left -= chunk;
offset += chunk;
page_idx++;
}
for (u64 i = 0; i < (stack_size / 4096); i++) {
void* phys = pmm_alloc_page();
vmm_map_page(pml4_kernel, (u64)phys, virt_stack + (i * 4096), PTE_PRESENT | PTE_RW | PTE_USER);
memset((void*)(HHDM_OFFSET + (u64)phys), 0, 4096);
}
__asm__ volatile(
"mov %%cr3, %%rax\n\t"
"mov %%rax, %%cr3\n\t"
::: "rax", "memory"
);
kprintf("[Loader] Transferring control to userspace...\n");
jump_to_userspace((void*)virt_code, (void*)(virt_stack + stack_size));
return true; // unreachable
}
void init_task_entry() {
if (!exec_init("/init")) {
kprintf("FATAL: Could not load /init\n");
sched_spawn(ksh);
while(1) __asm__("hlt");
}
}
+2
View File
@@ -4,6 +4,7 @@
#include <core/panic.h>
#include <core/scheduler.h>
#include <mm/heap.h>
#include <cpuinfo.h>
#include <gdt.h>
task* curr_task = nullptr;
@@ -68,6 +69,7 @@ u64 sched_next(u64 curr_rsp) {
curr_task = next;
tss.rsp0 = curr_task->kernel_stack_top;
g_cpu.kernel_rsp = curr_task->kernel_stack_top;
return curr_task->rsp;
}
+34
View File
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#include <core/userspace.h>
#include <types.h>
#define USER_DS (0x18 | 3)
#define USER_CS (0x20 | 3)
#define RFLAGS_IF 0x202
void jump_to_userspace(void* entry, void* user_stack_top) {
__asm__ volatile (
"mov %0, %%ds\n"
"mov %0, %%es\n"
"mov %0, %%fs\n"
:: "r" ((u64)USER_DS) : "memory"
);
__asm__ volatile (
"pushq %0\n" // SS (User Data Selector)
"pushq %1\n" // RSP (User Stack Pointer)
"pushq %2\n" // RFLAGS
"pushq %3\n" // CS (User Code Selector)
"pushq %4\n" // RIP (Entry Point)
"iretq\n"
:
: "r" ((u64)USER_DS),
"r" ((u64)user_stack_top),
"r" ((u64)RFLAGS_IF),
"r" ((u64)USER_CS),
"r" ((u64)entry)
: "memory"
);
}
-63
View File
@@ -8,11 +8,8 @@
#include <mm/memory.h>
#define SHITGUI_TRANSPARENCY_KEY 0xFF00FF
#define SHITGUI_MAX_WINDOWS_AMOUNT 64
SG_Context* main_context;
SG_Window* windows[SHITGUI_MAX_WINDOWS_AMOUNT]; // нет блять линукс
u64 windows_amount = 0;
void sg_init(SG_Context *ctx) {
main_context = ctx;
@@ -85,64 +82,4 @@ void sg_draw_char_bitmap(SG_Context *ctx, SG_Point *p, char c, u32 color, SG_Fon
}
}
}
}
SG_Window* create_window(const char *title, SG_Point* size, SG_Point* position) {
if (windows_amount >= SHITGUI_MAX_WINDOWS_AMOUNT) return nullptr;
SG_Context* ctx = nullptr;
u32* fb = nullptr;
SG_Window* window = nullptr;
ctx = (SG_Context*)malloc(sizeof(SG_Context));
if (!ctx) goto fail; // goto is GOOD for cleaning up in C stfu.
// Mainline linux kernel contains 100k gotos
fb = malloc(size->x * size->y * sizeof(u32));
if (!fb) goto fail;
memset(fb, 0, size->x * size->y * sizeof(u32));
window = (SG_Window*)malloc(sizeof(SG_Window));
if (!window) goto fail;
ctx->fb = fb;
ctx->height = size->y;
ctx->width = size->x;
ctx->pitch = size->x;
window->ctx = ctx;
window->title = title;
window->pos.x = position->x;
window->pos.y = position->y;
windows[windows_amount] = window;
windows_amount++;
return window;
fail:
if (ctx) free(ctx);
if (fb) free(fb);
if (window) free(window);
return nullptr;
}
void render_window(SG_Context *ctx, SG_Window *window) {
SG_Image img = {0};
img.buffer = window->ctx->fb;
if (!img.buffer) panic("No img buffer found");
img.height = window->ctx->height;
img.width = window->ctx->width;
sg_put_img(ctx, &window->pos, &img);
}
void composer_task() {
while (true) {
for (u64 i = 0; i < windows_amount; i++) {
render_window(main_context, windows[i]);
}
yield(1);
}
}
+21 -5
View File
@@ -2,7 +2,7 @@
// Copyright (c) 2025 0xKarinyash
#include "bootinfo.h"
#include "core/rand.h"
#include <shell/ksh.h>
#include <types.h>
@@ -15,11 +15,14 @@
#include <core/panic.h>
#include <core/scheduler.h>
#include <core/splash.h>
#include <core/rand.h>
#include <core/loader.h>
#include <gdt.h>
#include <idt.h>
#include <pic.h>
#include <cpuinfo.h>
#include <syscall.h>
#include <mm/pmm.h>
#include <mm/vmm.h>
@@ -32,6 +35,7 @@
#define BG_COLOR 0x111111
extern u64 _kernel_end;
extern void* stack_top;
static SG_Context sg_ctx;
void kmain(Bootinfo* info) {
@@ -42,7 +46,7 @@ void kmain(Bootinfo* info) {
if (info->magic != BOOTINFO_MAGIC) panic("Corrupt bootinfo!");
cpuinfo_init();
cpuinfo_init((u64)&stack_top);
kprintf("Got CPUINFO\n");
rng_init();
kprintf("RNG initialized\n");
@@ -64,6 +68,7 @@ void kmain(Bootinfo* info) {
kprintf("Scheduler initialized\n");
sg_init(&sg_ctx);
kprintf("Shitgui initialized\n");
syscall_init();
info = (Bootinfo*)PHYS_TO_HHDM((u64)info);
@@ -85,9 +90,20 @@ void kmain(Bootinfo* info) {
show_splash(&sg_ctx);
sched_spawn(composer_task);
sched_spawn(ksh);
if (!info->initramfs.addr) kprintf("^rWARNING^!: Failed to load ^yinitramfs^!, VFS is empty.\n\n");
bool staying_in_ksh = false;
if (!info->initramfs.addr) {
kprintf("^rWARNING^!: Failed to load ^yinitramfs^!! Staying in kernel rescue shell!\n\n");
staying_in_ksh = true;
}
kprintf("Press any key to stay in ^gksh^!. \nPress ^yenter^! to continue booting in ring 3\n");
char c = '\n';
c = console_getc();
if (c != '\n') staying_in_ksh = true;
if (staying_in_ksh) sched_spawn(ksh);
else sched_spawn(init_task_entry);
__asm__ volatile("sti");
while (true) __asm__ volatile("hlt");
+1 -1
View File
@@ -35,7 +35,7 @@ void cmd_kfetch() {
kprintf("^p %s ^!\t\t^gUptime^!: %d seconds\n^!", ascii_logo[4], uptime_s);
kprintf("^p %s ^!\t\t^gShell^!: ksh\n^!", ascii_logo[5]);
kprintf("^p %s ^!\t\t^gDE^!: shitgui\n^!", ascii_logo[6]);
kprintf("^p %s ^!\t\t^gCPU^!: %s\n^!", ascii_logo[7], g_cpu.vendor);
kprintf("^p %s ^!\t\t^gCPU^!: %s (^yFamily^!: %d; ^yModel^!: %d)\n^!", ascii_logo[7], g_cpu.vendor, g_cpu.family, g_cpu.model);
kprintf("\n\n");
}
+1 -49
View File
@@ -1,57 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2025 0xKarinyash
// contents of this file will be changed CONSTANTLY
// im just testing new stuff here
// Copyright (c) 2026 0xKarinyash
#include "mm/vmm.h"
#include <mm/pmm.h>
#include <shell/dbgcmd.h>
#include <drivers/console.h>
extern u64* pml4_kernel;
#define USER_DS (0x18 | 3)
#define USER_CS (0x20 | 3)
#define RFLAGS_IF 0x202
void jump_to_userspace(void* entry, void* user_stack_top) {
__asm__ volatile (
"mov %0, %%ds\n"
"mov %0, %%es\n"
"mov %0, %%fs\n"
"mov %0, %%gs\n"
:: "r" ((u64)USER_DS) : "memory"
);
__asm__ volatile (
"pushq %0\n" // SS (User Data Selector)
"pushq %1\n" // RSP (User Stack Pointer)
"pushq %2\n" // RFLAGS
"pushq %3\n" // CS (User Code Selector)
"pushq %4\n" // RIP (Entry Point)
"iretq\n" // jump
:
: "r" ((u64)USER_DS),
"r" ((u64)user_stack_top),
"r" ((u64)RFLAGS_IF),
"r" ((u64)USER_CS),
"r" ((u64)entry)
: "memory"
);
}
u64 debug() {
void* phys_code = pmm_alloc_page();
void* phys_stack = pmm_alloc_page();
u64 virt_code = 0x400000;
u64 virt_stack = 0x800000;
vmm_map_page(pml4_kernel, (u64)phys_code, virt_code, PTE_PRESENT | PTE_RW | PTE_USER);
vmm_map_page(pml4_kernel, (u64)phys_stack, virt_stack, PTE_PRESENT | PTE_RW | PTE_USER);
u8* kernel_view = (u8*)(HHDM_OFFSET + phys_code);
kernel_view[0] = 0xEB; // jmp
kernel_view[1] = 0xFE; // to self
kprintf("Jumping to userspace. Goodbye.");
jump_to_userspace((void*)virt_code, (void*)(virt_stack + 4096));
return 0;
}
+10
View File
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.20)
project(termOSuserspace LANGUAGES C ASM_NASM)
set(CMAKE_C_STANDARD 23)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
message(STATUS "Building termOS's userspace")
add_subdirectory(init)
+32
View File
@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.20)
project(termOSinit LANGUAGES C ASM_NASM)
set(CMAKE_C_STANDARD 23)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
message(STATUS "Building termOS's init")
file(GLOB_RECURSE INIT_SOURCES CMAKE_CONFIGURE_DEPENDS
"src/*.asm"
"src/*.c"
)
add_executable(init ${INIT_SOURCES})
set_target_properties(init PROPERTIES
SUFFIX ""
LINKER_LANGUAGE C
)
target_link_options(init PRIVATE
-nostdlib
-static
-Wl,--oformat=binary
-Wl,-Ttext=0x400000
-Wl,-e,start
)
add_custom_command(TARGET init POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:init> ${CMAKE_SOURCE_DIR}/initramfs/init
COMMENT "Installing init binary to initramfs"
)
+17
View File
@@ -0,0 +1,17 @@
[BITS 64]
start:
mov rax, 2 ; SYS_WRITE
mov rdi, msg_hello ; Адрес строки
syscall
mov rax, 1 ; SYS_EXEC
syscall
mov rax, 0 ; SYS_EXIT
mov rdi, 1337
syscall
jmp $
msg_hello: db "Hello from init!", 0xA, 0