diff --git a/.gitignore b/.gitignore index 6d2faec..963021a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build/ user/build -toolchains/ \ No newline at end of file +toolchains/ +linux-headers/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index da8c646..195c36d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,7 +137,7 @@ target_include_directories(KirkOS PRIVATE # ---------------------------------------- # DEFAULT C FLAGS (cacheable overrides) # ---------------------------------------- -set(KIRKOS_CFLAGS "-g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fno-PIC -ffunction-sections -fdata-sections -m64 -march=x86-64 -mabi=sysv -mno-80387 -mno-mmx -mno-sse -mno-sse2 -mno-red-zone -mcmodel=kernel" +set(KIRKOS_CFLAGS "-g -O2 -pipe -Wall -Wextra -std=gnu11 -ffreestanding -fno-stack-protector -fno-stack-check -fno-lto -fno-PIC -ffunction-sections -fdata-sections -m64 -march=x86-64 -mabi=sysv -mno-80387 -mno-red-zone -mcmodel=kernel" CACHE STRING "Default CFLAGS for KirkOS") set(KIRKOS_LDFLAGS "-nostdlib -static -Wl,--gc-sections -Wl,-T,../linker.lds -Wl,-z,max-page-size=0x1000 -Wl,-m,elf_x86_64" diff --git a/ext2_root/helloworld.elf b/ext2_root/helloworld.elf index efa54b6..5423367 100755 Binary files a/ext2_root/helloworld.elf and b/ext2_root/helloworld.elf differ diff --git a/ext2_root/usr/lib/libc.a b/ext2_root/usr/lib/libc.a index 826d7ed..34a9922 100644 Binary files a/ext2_root/usr/lib/libc.a and b/ext2_root/usr/lib/libc.a differ diff --git a/src/drivers/rand/random.c b/src/drivers/rand/random.c new file mode 100644 index 0000000..40cb784 --- /dev/null +++ b/src/drivers/rand/random.c @@ -0,0 +1,202 @@ +#include "random.h" +#include "fs/vfs.h" +#include "libk/stdio.h" +#include "arch/x86_64/sys/tsc.h" +#include "arch/x86_64/sys/pit.h" +#include "errno.h" +#include +#include +#include "limine.h" + +/* ChaCha20 core (public-domain, 20-round) */ +static inline void chacha20_quarterround(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) { + *a += *b; *d ^= *a; *d = (*d << 16) | (*d >> 16); + *c += *d; *b ^= *c; *b = (*b << 12) | (*b >> 20); + *a += *b; *d ^= *a; *d = (*d << 8) | (*d >> 24); + *c += *d; *b ^= *c; *b = (*b << 7) | (*b >> 25); +} + +static void chacha20_block(uint32_t state[16], uint8_t output[64]) { + uint32_t x[16]; + for (int i = 0; i < 16; ++i) x[i] = state[i]; + + for (int i = 0; i < 10; ++i) { /* 10 double-rounds = 20 rounds */ + /* column rounds */ + chacha20_quarterround(&x[0], &x[4], &x[8], &x[12]); + chacha20_quarterround(&x[1], &x[5], &x[9], &x[13]); + chacha20_quarterround(&x[2], &x[6], &x[10], &x[14]); + chacha20_quarterround(&x[3], &x[7], &x[11], &x[15]); + /* diagonal rounds */ + chacha20_quarterround(&x[0], &x[5], &x[10], &x[15]); + chacha20_quarterround(&x[1], &x[6], &x[11], &x[12]); + chacha20_quarterround(&x[2], &x[7], &x[8], &x[13]); + chacha20_quarterround(&x[3], &x[4], &x[9], &x[14]); + } + + for (int i = 0; i < 16; ++i) { + x[i] += state[i]; + ((uint32_t*)output)[i] = x[i]; + } +} + +/* Global ChaCha20 PRNG state */ +static uint8_t rng_key[32]; +static uint64_t rng_counter = 0; +static bool rng_seeded = false; + +static void rng_generate(uint8_t *buf, size_t len) { + size_t i = 0; + while (len > 0) { + uint32_t state[16]; + static const uint32_t sigma[4] = {0x61707865, 0x3320646e, 0x79622d32, 0x6b206574}; + + state[0] = sigma[0]; state[1] = sigma[1]; state[2] = sigma[2]; state[3] = sigma[3]; + for (int j = 0; j < 8; ++j) + state[4 + j] = ((uint32_t*)rng_key)[j]; + + state[12] = (uint32_t)rng_counter; + state[13] = (uint32_t)(rng_counter >> 32); + state[14] = 0xdeadbeef; /* fixed nonce (PRNG only) */ + state[15] = 0xbeefdead; + + uint8_t block[64]; + chacha20_block(state, block); + + size_t copy = (len < 64) ? len : 64; + for (size_t k = 0; k < copy; ++k) + buf[i + k] = block[k]; + + i += copy; + len -= copy; + rng_counter++; + } +} + +static void rng_add_entropy(const uint8_t *data, size_t len) { + if (len == 0 || !data) return; + + for (size_t i = 0; i < len; ++i) + rng_key[i % 32] ^= data[i]; + + /* stir (forward secrecy) */ + uint8_t discard[64]; + rng_generate(discard, 64); + + rng_seeded = true; +} + +int random_read(void* buf, size_t len) { + if (len == 0) return 0; + if (!buf) return -EFAULT; + if (!rng_seeded) return -EAGAIN; /* should never happen after init */ + + rng_generate((uint8_t*)buf, len); + return (int)len; +} + +int random_write(const void* buf, size_t len) { + if (len == 0) return 0; + if (!buf) return -EFAULT; + + rng_add_entropy((const uint8_t*)buf, len); + return (int)len; +} + +int getrandom(void *buf, size_t buflen, unsigned int flags) +{ + if (!buf) return -EFAULT; + if (buflen == 0) return 0; + + if (flags & GRND_INSECURE) { + // maybe fall back to weaker RNG? + } + + if (!rng_seeded) { + if (flags & GRND_NONBLOCK) + return -EAGAIN; + // could block here in the future + return -EAGAIN; + } + + rng_generate((uint8_t*)buf, buflen); + return (int)buflen; + +} + +void random_init(void) { + printf("Initializing ChaCha20-based CSPRNG for /dev/random and /dev/urandom...\n"); + + uint8_t entropy[64] = {0}; + size_t epos = 0; + + /* === SEEDING SOURCES === */ + + /* 1. Limine boot timestamp (wall-clock) */ + extern volatile struct limine_date_at_boot_request boot_request; + if (boot_request.response) { + uint64_t ts = boot_request.response->timestamp; + if (epos + sizeof(ts) <= sizeof(entropy)) { + memcpy(entropy + epos, &ts, sizeof(ts)); + epos += sizeof(ts); + } + } + + /* 2. Multiple TSC samples (high-resolution jitter) */ + for (int i = 0; i < 4; ++i) { + uint64_t tsc_val = rdtsc(); + if (epos + sizeof(tsc_val) <= sizeof(entropy)) { + memcpy(entropy + epos, &tsc_val, sizeof(tsc_val)); + epos += sizeof(tsc_val); + } + } + + /* 3. PIT tick counter */ + uint64_t pit_ticks = PIT_GetTicks(); + if (epos + sizeof(pit_ticks) <= sizeof(entropy)) { + memcpy(entropy + epos, &pit_ticks, sizeof(pit_ticks)); + epos += sizeof(pit_ticks); + } + + /* 4. RDRAND (hardware TRNG) if present */ + { + uint32_t eax, ebx, ecx, edx; + asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1)); + if (ecx & (1u << 30)) { + printf("RDRAND detected - using hardware RNG for seeding\n"); + for (int i = 0; i < 4 && epos + 8 <= sizeof(entropy); ++i) { + uint64_t rd; + asm volatile("1: rdrand %0\n\tjnc 1b" : "=r"(rd)); + memcpy(entropy + epos, &rd, sizeof(rd)); + epos += sizeof(rd); + } + } + } + + /* 5. Unix seconds from PIT (time-based entropy) */ + extern uint64_t g_Unixseconds; + uint64_t unix_sec = g_Unixseconds; + if (epos + sizeof(unix_sec) <= sizeof(entropy)) { + memcpy(entropy + epos, &unix_sec, sizeof(unix_sec)); + epos += sizeof(unix_sec); + } + + /* 6. Final TSC padding (extra jitter) */ + while (epos < 32) { + uint64_t t = rdtsc(); + memcpy(entropy + epos, &t, (epos + 8 <= 32) ? 8 : (32 - epos)); + epos += (epos + 8 <= 32) ? 8 : (32 - epos); + } + + /* Initialise ChaCha20 key from collected entropy */ + memcpy(rng_key, entropy, 32); + rng_counter = rdtsc(); /* random starting counter */ + rng_seeded = true; + + printf("ChaCha20 RNG seeded with %zu bytes of entropy (TSC, PIT, RDRAND, boot time, unix time, etc.)\n", epos); + + /* Register both devices (open as "/dev/random" and "/dev/urandom") */ + VFS_RegisterCharDev("/dev/random", random_read, random_write); + VFS_RegisterCharDev("/dev/urandom", random_read, random_write); + + printf("Registered chardevs: /dev/random and /dev/urandom (ChaCha20 CSPRNG)\n"); +} \ No newline at end of file diff --git a/src/drivers/rand/random.h b/src/drivers/rand/random.h new file mode 100644 index 0000000..40bf5e1 --- /dev/null +++ b/src/drivers/rand/random.h @@ -0,0 +1,12 @@ +#pragma once +#include +#include + +#define GRND_NONBLOCK 0x0001 +#define GRND_RANDOM 0x0002 +#define GRND_INSECURE 0x0004 + +void random_init(void); +int random_read(void* buf, size_t len); +int random_write(const void* buf, size_t len); +int getrandom(void *buf, size_t buflen, unsigned int flags); \ No newline at end of file diff --git a/src/fs/ext2.c b/src/fs/ext2.c index 608af13..409bb4e 100644 --- a/src/fs/ext2.c +++ b/src/fs/ext2.c @@ -100,7 +100,6 @@ bool ext2_read_superblock(void) { static bool ext2_write_superblock_internal(void) { uint8_t* tmp = kmalloc(1024); - printf("test4"); if (!tmp) return false; memset(tmp, 0, 1024); memcpy(tmp, &sb, sizeof(ext2_superblock_t)); @@ -179,11 +178,8 @@ static bool ext2_write_gdt(void) { } static void ext2_sync(void) { - printf("test1"); ext2_write_superblock_internal(); - printf("test2"); ext2_write_gdt_internal(); - printf("test3"); } // ── bitmap allocators ───────────────────────────────────────────────────────── @@ -344,8 +340,6 @@ bool ext2_read_inode_internal(uint32_t inum, ext2_inode_t* out) { if (!ext2_read_block_raw(gdt[g].bg_inode_table + block_off, buf)) { kfree(buf); return false; } - printf("ext2_read_inode: inum=%u group=%u idx=%u block_off=%u inode_off=%u\n", - inum + 1, g, idx, block_off, inode_off); memcpy(out, buf + inode_off * sb.s_inode_size, sizeof(ext2_inode_t)); kfree(buf); return true; @@ -537,8 +531,8 @@ static void ext2_free_inode_blocks(ext2_inode_t* inode) { // ── file I/O ───────────────────────────────────────────────────────────────── bool ext2_read_file_internal(ext2_inode_t* inode, uint8_t* buf) { - if ((inode->i_mode & 0xF000) != EXT2_S_IFREG) { - printf("EXT2: not a regular file\n"); + if (((inode->i_mode & 0xF000) != EXT2_S_IFREG) && ((inode->i_mode & 0xF000) != EXT2_S_IFDIR)) { + printf("EXT2: not a regular file or directory\n"); return false; } uint32_t size = inode->i_size; @@ -1012,7 +1006,7 @@ bool ext2_write_file_from_root(const char* name, const uint8_t* data, // ── create / delete ─────────────────────────────────────────────────────────── bool ext2_create_file_internal(ext2_inode_t* dir, uint32_t dir_inum, - const char* name, uint32_t* out_inum) + const char* name, uint16_t mode, uint32_t* out_inum) { if (dir_lookup_internal(dir, name)) { printf("EXT2: already exists: %s\n", name); @@ -1023,7 +1017,7 @@ bool ext2_create_file_internal(ext2_inode_t* dir, uint32_t dir_inum, if (!inum) return false; ext2_inode_t inode = {0}; - inode.i_mode = EXT2_S_IFREG | 0644; + inode.i_mode = mode; inode.i_links_count = 1; inode.i_atime = inode.i_ctime = inode.i_mtime = (uint32_t)g_Unixseconds; ext2_write_inode_internal(inum, &inode); @@ -1038,11 +1032,11 @@ bool ext2_create_file_internal(ext2_inode_t* dir, uint32_t dir_inum, } bool ext2_create_file(ext2_inode_t* dir, uint32_t dir_inum, - const char* name, uint32_t* out_inum) + const char* name, uint16_t mode, uint32_t* out_inum) { uint64_t flags; spinlock_acquire_irqsave(&s_ext2_lock, &flags); - bool resp = ext2_create_file_internal(dir, dir_inum, name, out_inum); + bool resp = ext2_create_file_internal(dir, dir_inum, name, mode, out_inum); spinlock_release_irqrestore(&s_ext2_lock, flags); return resp; } diff --git a/src/fs/ext2.h b/src/fs/ext2.h index 36a120d..ce8f060 100644 --- a/src/fs/ext2.h +++ b/src/fs/ext2.h @@ -198,7 +198,7 @@ bool ext2_write_file_from_root(const char* name, const uint8_t* data, // ── create / delete ─────────────────────────────────────────────────────────── bool ext2_create_file(ext2_inode_t* dir, uint32_t dir_inum, - const char* name, uint32_t* out_inum); + const char* name, uint16_t mode, uint32_t* out_inum); bool ext2_mkdir(ext2_inode_t* parent, uint32_t parent_inum, const char* name, uint32_t* out_inum); bool ext2_unlink(ext2_inode_t* dir, uint32_t dir_inum, const char* name); diff --git a/src/fs/vfs.c b/src/fs/vfs.c index a496c34..89a1be3 100644 --- a/src/fs/vfs.c +++ b/src/fs/vfs.c @@ -5,10 +5,12 @@ #include "libk/stdio.h" #include "mm/memory.h" #include "libk/string.h" +#include "libk/errno.h" #include "drivers/input/input.h" static spinlock_t s_vfs_lock = SPINLOCK_INIT; static vfs_file_t vfs_fd_table[VFS_MAX_FDS]; +vfs_dir_t vfs_dir_table[VFS_MAX_DIRS]; typedef struct { const char* path; @@ -26,7 +28,7 @@ int VFS_RegisterCharDev(const char* path, { if (s_num_chardevs >= VFS_MAX_CHARDEVS) { printf("VFS: too many char devices!\n"); - return -1; + return -ENFILE; } s_chardevs[s_num_chardevs].path = path; s_chardevs[s_num_chardevs].read = read; @@ -36,11 +38,11 @@ int VFS_RegisterCharDev(const char* path, return 0; } -fd_t VFS_Open_internal(const char* path) +static fd_t VFS_Open_internal(const char* path, int flags, unsigned int mode) { const char* p = path; - if (*p == '/') p++; + // ── chardevs (flags/mode ignored for now) ─────────────────────────────── for (int i = 0; i < s_num_chardevs; i++) { if (strcmp(s_chardevs[i].path, p) == 0) { for (int fd = 4; fd < VFS_MAX_FDS; fd++) { @@ -52,48 +54,81 @@ fd_t VFS_Open_internal(const char* path) return fd; } } - return -1; + return -EMFILE; } } - - + // ── EXT2 files ────────────────────────────────────────────────────────── uint32_t ino = ext2_resolve_path(p); - if (ino == 0) - return -1; - // find free fd - for (int i = 4; i < VFS_MAX_FDS; i++) - { - if (!vfs_fd_table[i].used) - { + if (ino != 0) { + // file exists + if ((flags & O_CREAT) && (flags & O_EXCL)) { + return -EEXIST; + } + + // O_TRUNC ? + if (flags & O_TRUNC) { + ext2_inode_t inode; + if (!ext2_read_inode(ino, &inode)) { + return -EIO; + } + if (!ext2_truncate(&inode, ino, 0)) { + return -EIO; + } + } + } else { + // does not exist + if (!(flags & O_CREAT)) { + return -ENOENT; + } + + // Create new regular file in root (simple filename only for now) + ext2_inode_t root; + if (!ext2_read_inode(2, &root)) { + return -EIO; + } + + uint32_t new_ino; + uint16_t file_mode = EXT2_S_IFREG | (mode & 0777); // respect user-supplied perms + + if (!ext2_create_file(&root, 2, p, file_mode, &new_ino)) { + return -EIO; + } + ino = new_ino; + } + + // ── allocate fd and fill table ────────────────────────────────────────── + for (int i = 4; i < VFS_MAX_FDS; i++) { + if (!vfs_fd_table[i].used) { vfs_fd_table[i].used = true; - vfs_fd_table[i].type = VFS_NODE_EXT2; + vfs_fd_table[i].type = VFS_NODE_FILE; vfs_fd_table[i].offset = 0; vfs_fd_table[i].ext2.inode_num = ino; - if (!ext2_read_inode(ino, &vfs_fd_table[i].ext2.inode)) - return -1; - + if (!ext2_read_inode(ino, &vfs_fd_table[i].ext2.inode)) { + vfs_fd_table[i].used = false; + return -EIO; + } return i; } } - return -1; + return -EMFILE; } -fd_t VFS_Open(const char* path) { - uint64_t flags; - spinlock_acquire_irqsave(&s_vfs_lock, &flags); - fd_t fd = VFS_Open_internal(path); - spinlock_release_irqrestore(&s_vfs_lock, flags); +fd_t VFS_Open(const char* path, int flags, unsigned int mode) { + uint64_t spin_flags; + spinlock_acquire_irqsave(&s_vfs_lock, &spin_flags); + fd_t fd = VFS_Open_internal(path, flags, mode); + spinlock_release_irqrestore(&s_vfs_lock, spin_flags); return fd; } int VFS_Close_internal(fd_t fd) { if (fd < 0 || fd >= VFS_MAX_FDS) - return -1; + return -EBADF; vfs_fd_table[fd].used = false; return 0; @@ -110,7 +145,7 @@ int VFS_Close(fd_t fd) { int VFS_Read_internal(fd_t fd, uint8_t* buf, size_t size) { if (fd < 0 || fd >= VFS_MAX_FDS) - return -1; + return -EBADF; if (fd == VFS_FD_STDIN) { return input_read_console(buf, size); @@ -119,11 +154,11 @@ int VFS_Read_internal(fd_t fd, uint8_t* buf, size_t size) vfs_file_t* file = &vfs_fd_table[fd]; if (!file->used) - return -1; + return -EBADF; switch (file->type) { - case VFS_NODE_EXT2: + case VFS_NODE_FILE: { uint32_t file_size = file->ext2.inode.i_size; @@ -137,11 +172,11 @@ int VFS_Read_internal(fd_t fd, uint8_t* buf, size_t size) // naive: read whole file then slice uint8_t* tmp = kmalloc(file_size); if (!tmp) { - return -1; + return -ENOMEM; } if (!ext2_read_file(&file->ext2.inode, tmp)) { kfree(tmp); - return -1; + return -EIO; } for (size_t i = 0; i < size; i++) @@ -156,12 +191,12 @@ int VFS_Read_internal(fd_t fd, uint8_t* buf, size_t size) { int idx = file->chardev.dev_idx; if (idx < 0 || idx >= s_num_chardevs || !s_chardevs[idx].read) - return -1; + return -ENXIO; return s_chardevs[idx].read(buf, size); } default: - return -1; + return -EINVAL; } } @@ -179,15 +214,14 @@ int VFS_Write_internal(fd_t file, uint8_t* data, size_t size) switch (file) { case VFS_FD_STDIN: - return 0; + return -EINVAL; case VFS_FD_STDOUT: - for (size_t i = 0; i < size; i++) - putchar(data[i]); - return size; case VFS_FD_STDERR: - for (size_t i = 0; i < size; i++) + for (size_t i = 0; i < size; i++) { putchar(data[i]); + e9_putc(data[i]); + } return size; case VFS_FD_DEBUG: @@ -200,14 +234,14 @@ int VFS_Write_internal(fd_t file, uint8_t* data, size_t size) } if (file < 0 || file >= VFS_MAX_FDS) - return -1; + return -EBADF; vfs_file_t* f = &vfs_fd_table[file]; if (!f->used) - return -1; + return -EBADF; - if (f->type == VFS_NODE_EXT2) + if (f->type == VFS_NODE_FILE) { if (!ext2_write_file( &f->ext2.inode, @@ -215,7 +249,7 @@ int VFS_Write_internal(fd_t file, uint8_t* data, size_t size) data, size, EXT2_WRITE_APPEND)) - return -1; + return -EIO; return size; } @@ -225,10 +259,10 @@ int VFS_Write_internal(fd_t file, uint8_t* data, size_t size) int idx = f->chardev.dev_idx; if (idx >= 0 && idx < s_num_chardevs && s_chardevs[idx].write) return s_chardevs[idx].write(data, size); - return -1; + return -ENXIO; } - return -1; + return -EINVAL; } int VFS_Write(fd_t file, uint8_t* data, size_t size) diff --git a/src/fs/vfs.h b/src/fs/vfs.h index 6b70c93..e2d970f 100644 --- a/src/fs/vfs.h +++ b/src/fs/vfs.h @@ -4,10 +4,27 @@ #include #include "ext2.h" + +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#define O_ACCMODE 3 +#define O_CREAT 0100 +#define O_EXCL 0200 +#define O_NOCTTY 0400 +#define O_TRUNC 01000 +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_DSYNC 010000 + typedef int fd_t; #define VFS_MAX_FDS 32 +#define VFS_MAX_DIRS 64 + + + #define VFS_FD_STDIN 0 #define VFS_FD_STDOUT 1 #define VFS_FD_STDERR 2 @@ -15,7 +32,8 @@ typedef int fd_t; typedef enum { VFS_NODE_NONE, - VFS_NODE_EXT2, + VFS_NODE_FILE, + VFS_NODE_DIR, VFS_NODE_CHARDEV } vfs_node_type_t; @@ -37,8 +55,33 @@ typedef struct { bool used; } vfs_file_t; +typedef struct { + bool used; + uint32_t inode; + size_t offset; + vfs_node_type_t type; +} vfs_dir_t; + +typedef uint64_t ino_t; +typedef int64_t off_t; + + +#define IFTODT(mode) (((mode) & 0170000) >> 12) + +#define __MLIBC_DIRENT_BODY ino_t d_ino; \ + off_t d_off; \ + unsigned short d_reclen; \ + unsigned char d_type; \ + char d_name[1024]; + +struct dirent { + __MLIBC_DIRENT_BODY +}; + +extern vfs_dir_t vfs_dir_table[VFS_MAX_DIRS]; + // API -fd_t VFS_Open(const char* path); +fd_t VFS_Open(const char* path, int flags, unsigned int mode); int VFS_Read(fd_t fd, uint8_t* buf, size_t size); int VFS_Write(fd_t fd, uint8_t* data, size_t size); int VFS_Close(fd_t fd); diff --git a/src/main.c b/src/main.c index 7904ed9..f2045b2 100644 --- a/src/main.c +++ b/src/main.c @@ -32,6 +32,7 @@ #include "arch/x86_64/sys/ioapic.h" #include "drivers/input/ps2.h" #include "sched/scheduler.h" +#include "drivers/rand/random.h" uintptr_t g_hhdm_offset; @@ -89,6 +90,7 @@ __attribute__((used, section(".limine_requests_end"))) static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER; +uint8_t g_clean_fxstate[512] __attribute__((aligned(16))); // Halt and catch fire function. @@ -211,6 +213,8 @@ void init_simd(void) { // Initialize FPU/SSE state __asm__ volatile ("fninit"); + + __asm__ volatile ("fxsave %0" : "=m"(g_clean_fxstate)); } @@ -432,17 +436,19 @@ void kmain(void) { input_init(); ps2_kbd_init(); + random_init(); gdt_load_tss((size_t)&kernel_tss); kernel_tss.rsp0 = rsp; syscall_init(); + enable_fsgsbase_if_supported(); + init_simd(); sched_init(); - enable_fsgsbase_if_supported(); - init_simd(); + start_userspace(); diff --git a/src/sched/context_switch.S b/src/sched/context_switch.S index 7d44db2..12518b6 100644 --- a/src/sched/context_switch.S +++ b/src/sched/context_switch.S @@ -42,6 +42,8 @@ sched_context_switch: # from->rsp = RSP (rdi = struct cpu_context *from, offset 0 = rsp) movq %rsp, 0(%rdi) + fxsave 16(%rdi) + # ── Switch address space (CR3) if needed ─────────────────────────── # to->cr3 is at offset 8 in struct cpu_context movq 8(%rsi), %rax @@ -59,6 +61,8 @@ sched_context_switch: # to->rsp is at offset 0 (rsi = struct cpu_context *to) movq 0(%rsi), %rsp + fxrstor 16(%rsi) + # ── Restore incoming task's callee-saved registers ───────────────── popq %r15 popq %r14 diff --git a/src/sched/scheduler.c b/src/sched/scheduler.c index 3d87a41..bec6622 100644 --- a/src/sched/scheduler.c +++ b/src/sched/scheduler.c @@ -379,6 +379,8 @@ static task_t *alloc_task(const char *name, bool is_user) { if (!task) return NULL; memset(task, 0, sizeof(task_t)); + memcpy(task->ctx.fxstate, g_clean_fxstate, 512); + strncpy(task->name, name, sizeof(task->name) - 1); task->is_user = is_user; task->state = TASK_RUNNING; @@ -491,6 +493,7 @@ void sched_init(void) { while (1) asm volatile("hlt"); } memset(boot, 0, sizeof(task_t)); + memcpy(boot->ctx.fxstate, g_clean_fxstate, 512); strncpy(boot->name, "boot", 63); boot->pid = alloc_pid(); /* pid 1 */ boot->state = TASK_RUNNING; diff --git a/src/sched/scheduler.h b/src/sched/scheduler.h index 2ebde48..b2f6973 100644 --- a/src/sched/scheduler.h +++ b/src/sched/scheduler.h @@ -41,6 +41,9 @@ #define SIGSYS 31 #define _NSIG 32 +/* Clean FXSAVE state used to initialize every new task */ +extern uint8_t g_clean_fxstate[512] __attribute__((aligned(16))); + typedef void (*sighandler_t)(int signum); #define SIG_DFL ((sighandler_t)0) /* default action */ #define SIG_IGN ((sighandler_t)1) /* ignore signal */ @@ -116,6 +119,7 @@ typedef enum task_state { struct cpu_context { uint64_t rsp; /* Saved kernel stack pointer */ uint64_t cr3; /* Physical address of PML4 (0 = stay on kernel map) */ + uint8_t fxstate[512] __attribute__((aligned(16))); /* FPU/SSE state (FXSAVE area) */ }; /* ===================================================================== diff --git a/src/sched/user_task_trampoline.S b/src/sched/user_task_trampoline.S index 462ec34..8dbbc48 100644 --- a/src/sched/user_task_trampoline.S +++ b/src/sched/user_task_trampoline.S @@ -1,14 +1,36 @@ /* ── struct task offsets ─────────────────────────────────────────────────── */ -.equ TASK_KERNEL_STACK, 160 -.equ TASK_KERNEL_STACK_SIZE, 168 -.equ TASK_USER_ENTRY, 176 -.equ TASK_USER_STACK_TOP, 184 -.equ TASK_TLS_FS_BASE, 208 - -.equ TASK_PHDR_VA, 216 -.equ TASK_PHENT, 224 -.equ TASK_PHNUM, 226 + +.equ TASK_CTX, 0 +.equ TASK_CTX_RSP, 0 +.equ TASK_CTX_CR3, 8 +.equ TASK_CTX_FXSTATE, 16 + +.equ TASK_PID, 528 /* update if` struct changes */ +.equ TASK_PPID, 532 +.equ TASK_NAME, 536 +.equ TASK_IS_USER, 600 +.equ TASK_POLICY, 604 +.equ TASK_STATIC_PRIO, 608 +.equ TASK_NICE, 612 +.equ TASK_PRIO, 616 +.equ TASK_STATE, 620 # enum (4 bytes) +.equ TASK_NEED_RESCHED, 624 # bool (1 byte) +.equ TASK_VRUNTIME, 632 +.equ TASK_SUM_EXEC_RUNTIME,640 +.equ TASK_TIME_SLICE, 648 +.equ TASK_SLICE_START, 656 +.equ TASK_PAGEMAP, 664 +.equ TASK_KERNEL_STACK, 672 +.equ TASK_KERNEL_STACK_SIZE,680 +.equ TASK_USER_ENTRY, 688 +.equ TASK_USER_STACK_TOP, 696 +.equ TASK_KTHREAD_ENTRY, 704 +.equ TASK_KTHREAD_ARG, 712 +.equ TASK_TLS_FS_BASE, 720 +.equ TASK_PHDR_VA, 728 +.equ TASK_PHENT, 736 # uint16_t +.equ TASK_PHNUM, 738 # uint16_t /* ── GDT selectors ───────────────────────────────────────────────────────── */ .equ SEL_USER_DS, 0x1B /* ring-3 data (index 3, RPL 3) */ diff --git a/src/syscall/syscall.c b/src/syscall/syscall.c index 6a8d628..438e98d 100644 --- a/src/syscall/syscall.c +++ b/src/syscall/syscall.c @@ -9,6 +9,7 @@ #include "mm/memory.h" #include "libk/errno.h" #include "mp/futex.h" +#include "drivers/rand/random.h" #define MSR_EFER 0xC0000080 #define MSR_STAR 0xC0000081 @@ -63,12 +64,145 @@ uint64_t syscall_handler(uint64_t num, case SYS_OPEN: { const char* path = (const char*)arg1; - return (uint64_t)VFS_Open(path); + int flags = (int)arg2; + unsigned int mode = (unsigned int)arg3; + return (uint64_t)VFS_Open(path, flags, mode); + } + case SYS_OPEN_DIR: + { + const char* path = (const char*)arg1; + int* out_handle = (int*)arg2; + + if (!path || !out_handle) + return -EINVAL; + + uint32_t ino = ext2_resolve_path(path); + if (ino == 0) + return -ENOENT; + + ext2_inode_t inode; + if (!ext2_read_inode(ino, &inode)) + return -EIO; + + if (!(inode.i_mode & EXT2_S_IFDIR)) + return -ENOTDIR; + + // allocate dir handle (safe offset so it never collides with real fds) + for (int i = 0; i < VFS_MAX_DIRS; i++) + { + if (!vfs_dir_table[i].used) + { + vfs_dir_table[i].used = true; + vfs_dir_table[i].inode = ino; + vfs_dir_table[i].offset = 0; + vfs_dir_table[i].type = 1; + + *out_handle = VFS_MAX_FDS + i; // e.g. 32 + i + return 0; + } + } + + return -EMFILE; + } + + case SYS_READ_ENTRIES: + { + int handle = (int)arg1; + void *buf = (void*)arg2; + size_t max = (size_t)arg3; + size_t *out = (size_t*)arg4; + + if (!buf || !out) + return -EINVAL; + + // Directory handles are at VFS_MAX_FDS + idx + int idx = handle - VFS_MAX_FDS; + if (idx < 0 || idx >= VFS_MAX_DIRS) { + return -EBADF; + } + + vfs_dir_t *d = &vfs_dir_table[idx]; + if (!d->used) { + return -EBADF; + } + + ext2_inode_t dir_inode; + if (!ext2_read_inode(d->inode, &dir_inode)) { + return -EIO; + } + + if (dir_inode.i_size == 0) { + *out = 0; + return 0; + } + + uint8_t *tmp = kmalloc(dir_inode.i_size); + if (!tmp) { + return -ENOMEM; + } + + // Read the raw directory blocks (same format as regular files) + if (!ext2_read_file(&dir_inode, tmp)) { + kfree(tmp); + return -EIO; + } + + size_t pos = 0; + size_t out_pos = 0; + size_t index = 0; + + while (pos < dir_inode.i_size && out_pos + sizeof(struct dirent) < max) + { + ext2_dir_entry_t *e = (ext2_dir_entry_t*)(tmp + pos); + + if (e->inode != 0) + { + if (index++ >= d->offset) + { + struct dirent *out_ent = (struct dirent*)((uint8_t*)buf + out_pos); + + memset(out_ent, 0, sizeof(struct dirent)); + + out_ent->d_ino = e->inode; + out_ent->d_off = index; + out_ent->d_reclen = sizeof(struct dirent); + out_ent->d_type = IFTODT(e->file_type); + + size_t n = e->name_len; + if (n >= sizeof(out_ent->d_name)) + n = sizeof(out_ent->d_name) - 1; + + memcpy(out_ent->d_name, e->name, n); + out_ent->d_name[n] = '\0'; + + out_pos += sizeof(struct dirent); + } + } + + pos += e->rec_len; + } + + d->offset = index; + + *out = out_pos; + + kfree(tmp); + return 0; } case SYS_CLOSE: { int fd = (int)arg1; + + if (fd >= VFS_MAX_FDS && fd < VFS_MAX_FDS + VFS_MAX_DIRS) { + int idx = fd - VFS_MAX_FDS; + if (idx >= 0 && idx < VFS_MAX_DIRS && vfs_dir_table[idx].used) { + vfs_dir_table[idx].used = false; + return 0; + } + return -EBADF; + } + return (uint64_t)VFS_Close(fd); } @@ -262,6 +396,16 @@ uint64_t syscall_handler(uint64_t num, } } + case SYS_GETRANDOM: + { + void *buf = (void*)arg1; + size_t buflen = (size_t)arg2; + unsigned int flags = (unsigned int)arg3; + + return (int64_t)getrandom(buf, buflen, flags); + } + + default: { printf("Unknown syscall: %lu\n", num); diff --git a/src/syscall/syscall.h b/src/syscall/syscall.h index dced74c..af0f8dc 100644 --- a/src/syscall/syscall.h +++ b/src/syscall/syscall.h @@ -25,8 +25,12 @@ #define SYS_FUTEX 202 #define SYS_EXIT_GROUP 231 #define SYS_TCB_SET 300 +#define SYS_GETRANDOM 318 +#define SYS_OPEN_DIR 319 +#define SYS_READ_ENTRIES 320 + + -typedef int64_t off_t; // Memory protection flags (Linux compatible) diff --git a/user/include/mlibc/helloworld.c b/user/include/mlibc/helloworld.c index 00aa25e..711832c 100644 --- a/user/include/mlibc/helloworld.c +++ b/user/include/mlibc/helloworld.c @@ -1,7 +1,151 @@ -#include +#include +#include +#include +#include +#include +#include +#include -int main(void) { - printf("Hello, world!\n"); - return 0; +#define STDIN 0 +#define STDOUT 1 + +static void print(const char* s) +{ + write(STDOUT, s, strlen(s)); } +static void touch(const char* path) +{ + int fd = open(path, O_CREAT | O_WRONLY, 0644); + if (fd < 0) { + print("touch: cannot create file\n"); + return; + } + + close(fd); +} + +static void ls(const char* path) +{ + struct dirent* entry; + + DIR* dir = opendir(path ? path : "."); + if (!dir) { + print("ls: cannot open directory\n"); + return; + } + + while ((entry = readdir(dir)) != NULL) + { + // skip . and .. + if (entry->d_name[0] == '.' && + (entry->d_name[1] == '\0' || + (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) + continue; + + print(entry->d_name); + print("\n"); + } + + closedir(dir); +} + +static void readline(char* buf, size_t max) +{ + size_t i = 0; + + while (i < max - 1) + { + char c; + ssize_t n = read(STDIN, &c, 1); + + if (n <= 0) + continue; + + if (c == '\n') + break; + + if (c == '\b') { + if (i > 0) i--; + continue; + } + + buf[i++] = c; + } + + buf[i] = '\0'; +} + +static void cat(const char* path) +{ + char buf[256]; + + int fd = open(path, O_RDONLY); + if (fd < 0) { + print("cat: cannot open file\n"); + return; + } + + ssize_t n = read(fd, buf, sizeof(buf)); + close(fd); + + if (n > 0) + write(STDOUT, buf, (size_t)n); +} + +int main(void) +{ + const char* path = "/qwerty.txt"; + const char* msg = "Suki Suki Daisuki Kekkon Shiyo, my honey!"; + + char buf[128]; + + int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + write(fd, msg, strlen(msg)); + close(fd); + + fd = open(path, O_RDONLY); + ssize_t n = read(fd, buf, sizeof(buf)); + close(fd); + + if (n > 0) + write(STDOUT, buf, (size_t)n); + + char line[128]; + + while (1) + { + print("> "); + readline(line, sizeof(line)); + + if (strcmp(line, "exit") == 0) + break; + + if (line[0] == 'c' && line[1] == 'a' && line[2] == 't' && line[3] == ' ') + { + cat(&line[4]); + continue; + } + + if (line[0] == 't' && line[1] == 'o' && line[2] == 'u' && line[3] == 'c' && line[4] == 'h' && line[5] == ' ') + { + touch(&line[6]); + continue; + } + + if (line[0] == 'l' && line[1] == 's') + { + if (line[2] == ' ') + ls(&line[3]); + else + ls(NULL); + + continue; + } + + if (line[0] != '\0') + print("Unknown command\n"); + } + + return 0; +} \ No newline at end of file diff --git a/user/include/mlibc/sysdeps/kirkos/include/syscall.h b/user/include/mlibc/sysdeps/kirkos/include/syscall.h index 89d0c88..3ad0b87 100644 --- a/user/include/mlibc/sysdeps/kirkos/include/syscall.h +++ b/user/include/mlibc/sysdeps/kirkos/include/syscall.h @@ -24,4 +24,6 @@ #define SYS_FUTEX 202 #define SYS_EXIT_GROUP 231 #define SYS_TCB_SET 300 - +#define SYS_GETRANDOM 318 +#define SYS_OPEN_DIR 319 +#define SYS_READ_ENTRIES 320 diff --git a/user/include/mlibc/sysdeps/kirkos/sysdeps.cpp b/user/include/mlibc/sysdeps/kirkos/sysdeps.cpp index 506b3f0..d2a41f5 100644 --- a/user/include/mlibc/sysdeps/kirkos/sysdeps.cpp +++ b/user/include/mlibc/sysdeps/kirkos/sysdeps.cpp @@ -57,6 +57,11 @@ int sys_close(int fd) { return r >= 0 ? 0 : -r; } +int sys_getrandom(void *buffer, size_t length, int flags) { + long r = syscall(SYS_GETRANDOM, buffer, length, flags); + return r; +} + // ───────────────────────────────────────────────────────────────────────────── // Memory // ───────────────────────────────────────────────────────────────────────────── @@ -118,8 +123,7 @@ int sys_tcb_set(void *pointer) { } int sys_open(const char *path, int flags, unsigned int mode, int *fd) { - (void)flags; (void)mode; - long r = syscall(SYS_OPEN, (uintptr_t)path); + long r = syscall(SYS_OPEN, (uintptr_t)path, (long)flags, (long)mode); if (r < 0) return -r; *fd = (int)r; return 0; @@ -136,6 +140,20 @@ int sys_futex_wake(int *ptr) { return (r < 0) ? -r : 0; } +int sys_open_dir(const char *path, int *handle) { + long r = syscall(SYS_OPEN_DIR, (uintptr_t)path, (uintptr_t)handle); + return (r < 0) ? -r : 0; +} + +int sys_read_entries(int handle, void *buf, size_t bufsize, size_t *out) { + long r = syscall(SYS_READ_ENTRIES, handle, (uintptr_t)buf, bufsize, (uintptr_t)out); + if (r < 0) return -r; + + // Kernel already wrote the byte count into *out via the pointer. + // Do NOT overwrite it with r (which is always 0 on success). + return 0; +} + int sys_clock_get(int, time_t *, long *) { return ENOSYS; } } // namespace mlibc