src/kernel --> src/

This commit is contained in:
boreddevnl
2026-03-16 00:30:47 +01:00
parent 3da1496e4f
commit fc83d7941b
630 changed files with 2 additions and 2 deletions

116
src/sys/elf.c Normal file
View File

@@ -0,0 +1,116 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "elf.h"
#include "fat32.h"
#include "memory_manager.h"
#include "paging.h"
#include "platform.h"
extern void serial_print(const char *s);
extern void serial_write(const char *str);
uint64_t elf_load(const char *path, uint64_t user_pml4, size_t *out_load_size) {
if (out_load_size) *out_load_size = 0;
FAT32_FileHandle *file = fat32_open(path, "r");
if (!file || !file->valid) {
serial_write("[ELF] Error: Failed to open file ");
serial_write(path);
serial_write("\n");
return 0;
}
// Read the ELF Header
Elf64_Ehdr ehdr;
if (fat32_read(file, &ehdr, sizeof(Elf64_Ehdr)) != sizeof(Elf64_Ehdr)) {
serial_write("[ELF] Error: Could not read ELF Header\n");
fat32_close(file);
return 0;
}
// Validate Magic Number & Properties
if (ehdr.e_ident[0] != ELFMAG0 || ehdr.e_ident[1] != ELFMAG1 ||
ehdr.e_ident[2] != ELFMAG2 || ehdr.e_ident[3] != ELFMAG3) {
serial_write("[ELF] Error: Invalid ELF Magic Number\n");
fat32_close(file);
return 0;
}
if (ehdr.e_ident[4] != ELFCLASS64) {
serial_write("[ELF] Error: Not a 64-bit ELF\n");
fat32_close(file);
return 0;
}
if (ehdr.e_ident[5] != ELFDATA2LSB) {
serial_write("[ELF] Error: Not Little Endian\n");
fat32_close(file);
return 0;
}
if (ehdr.e_type != ET_EXEC && ehdr.e_type != ET_DYN) {
serial_write("[ELF] Error: Not an Executable\n");
fat32_close(file);
return 0;
}
if (ehdr.e_machine != EM_X86_64) {
serial_write("[ELF] Error: Not x86_64 Architecture\n");
fat32_close(file);
return 0;
}
// Iterate Over Program Headers
for (int i = 0; i < ehdr.e_phnum; i++) {
fat32_seek(file, ehdr.e_phoff + (i * ehdr.e_phentsize), 0);
Elf64_Phdr phdr;
if (fat32_read(file, &phdr, sizeof(Elf64_Phdr)) != sizeof(Elf64_Phdr)) {
serial_write("[ELF] Error: Failed to read Program Header\n");
continue;
}
// Only load segments with type PT_LOAD
if (phdr.p_type == PT_LOAD) {
uint64_t p_vaddr = phdr.p_vaddr;
uint64_t p_memsz = phdr.p_memsz;
uint64_t p_filesz = phdr.p_filesz;
uint64_t p_offset = phdr.p_offset;
if (p_memsz == 0) continue;
// Calculate boundaries for bulk allocation
uintptr_t align_offset = p_vaddr & 0xFFF;
uintptr_t start_page = p_vaddr & ~0xFFFFFFFFFFFFF000ULL; // Wait, mask should be ~0xFFF
start_page = p_vaddr & ~0xFFFULL;
size_t total_needed = (p_memsz + align_offset + 4095) & ~4095ULL;
size_t num_pages = total_needed / 4096;
// Bulk allocate physical memory for the entire segment
// Note: We allocate page by page but map them sequentially.
// A better way is to allocate a large contiguous block if possible,
// but our kmalloc_aligned handles arbitrary sizes.
void* bulk_phys = kmalloc_aligned(total_needed, 4096);
if (!bulk_phys) {
serial_write("[ELF] Error: Out of memory bulk allocating segment\n");
fat32_close(file);
return 0;
}
// Zero out entire segment (BSS and padding) in one go
mem_memset(bulk_phys, 0, total_needed);
// Bulk read from disk for the entire filesz part
if (p_filesz > 0) {
fat32_seek(file, p_offset, 0);
fat32_read(file, (uint8_t*)bulk_phys + align_offset, (uint32_t)p_filesz);
}
// Map all pages
for (uint64_t p = 0; p < num_pages; p++) {
uint64_t vaddr = start_page + (p * 4096);
uint64_t phys_addr = v2p((uint64_t)bulk_phys + (p * 4096));
paging_map_page(user_pml4, vaddr, phys_addr, 0x07);
}
if (out_load_size) *out_load_size += total_needed;
}
}
fat32_close(file);
return ehdr.e_entry;
}

78
src/sys/elf.h Normal file
View File

@@ -0,0 +1,78 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef ELF_H
#define ELF_H
#include <stdint.h>
typedef uint16_t Elf64_Half;
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
typedef uint64_t Elf64_Addr;
typedef uint64_t Elf64_Off;
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf64_Half e_type; /* Object file type */
Elf64_Half e_machine; /* Architecture */
Elf64_Word e_version; /* Object file version */
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags; /* Processor-specific flags */
Elf64_Half e_ehsize; /* ELF header size in bytes */
Elf64_Half e_phentsize; /* Program header table entry size */
Elf64_Half e_phnum; /* Program header table entry count */
Elf64_Half e_shentsize; /* Section header table entry size */
Elf64_Half e_shnum; /* Section header table entry count */
Elf64_Half e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;
typedef struct {
Elf64_Word p_type; /* Segment type */
Elf64_Word p_flags; /* Segment flags */
Elf64_Off p_offset; /* Segment file offset */
Elf64_Addr p_vaddr; /* Segment virtual address */
Elf64_Addr p_paddr; /* Segment physical address */
Elf64_Xword p_filesz; /* Segment size in file */
Elf64_Xword p_memsz; /* Segment size in memory */
Elf64_Xword p_align; /* Segment alignment */
} Elf64_Phdr;
/* e_ident constants */
#define ELFMAG0 0x7f
#define ELFMAG1 'E'
#define ELFMAG2 'L'
#define ELFMAG3 'F'
#define ELFCLASS64 2
#define ELFDATA2LSB 1
#define EV_CURRENT 1
/* e_type constants */
#define ET_EXEC 2
#define ET_DYN 3
/* e_machine constants */
#define EM_X86_64 62
/* p_type constants */
#define PT_LOAD 1
/* p_flags constants */
#define PF_X 1
#define PF_W 2
#define PF_R 4
#include <stdbool.h>
#include <stddef.h>
// Loads the ELF executable at 'path' using fat32 into the pagemap given by user_pml4.
// Returns entry point address on success, or 0 on failure.
uint64_t elf_load(const char *path, uint64_t user_pml4, size_t *out_load_size);
#endif

108
src/sys/gdt.c Normal file
View File

@@ -0,0 +1,108 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "gdt.h"
#include <stdint.h>
#include <stddef.h>
#include "memory_manager.h"
static void *gdt_memset(void *s, int c, size_t n) {
uint8_t *p = (uint8_t *)s;
while (n--) {
*p++ = (uint8_t)c;
}
return s;
}
#define GDT_ENTRIES 7
struct gdt_entry gdt[GDT_ENTRIES];
struct gdt_ptr gdtr;
struct tss_entry tss;
extern void gdt_flush(uint64_t);
extern void tss_flush(void);
static void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) {
gdt[num].base_low = (base & 0xFFFF);
gdt[num].base_middle = (base >> 16) & 0xFF;
gdt[num].base_high = (base >> 24) & 0xFF;
gdt[num].limit_low = (limit & 0xFFFF);
gdt[num].granularity = ((limit >> 16) & 0x0F);
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access = access;
}
static void gdt_set_tss_gate(int num, uint64_t base, uint32_t limit, uint8_t access, uint8_t gran) {
// A TSS entry in x86_64 is 16 bytes (takes up 2 adjacent GDT entries)
struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
uint32_t base_upper;
uint32_t reserved;
} __attribute__((packed)) *tss_desc = (void*)&gdt[num];
tss_desc->base_low = (base & 0xFFFF);
tss_desc->base_middle = (base >> 16) & 0xFF;
tss_desc->base_high = (base >> 24) & 0xFF;
tss_desc->base_upper = (base >> 32);
tss_desc->limit_low = (limit & 0xFFFF);
tss_desc->granularity = ((limit >> 16) & 0x0F);
tss_desc->granularity |= (gran & 0xF0);
tss_desc->access = access;
tss_desc->reserved = 0;
}
void tss_set_stack(uint64_t kernel_stack) {
tss.rsp0 = kernel_stack;
}
void gdt_init(void) {
gdtr.limit = (sizeof(struct gdt_entry) * GDT_ENTRIES) - 1;
gdtr.base = (uint64_t)&gdt;
// NULL segment
gdt_set_gate(0, 0, 0, 0, 0);
// Kernel Code segment (Ring 0, 64-bit)
// 0x9A: Present(1), Ring(0), System(1), Executable(1), DirConforming(0), Readable(1), Accessed(0)
// 0xAF: Long Mode (64-bit) (L=1, DB=0)
gdt_set_gate(1, 0, 0, 0x9A, 0xAF);
// Kernel Data segment (Ring 0)
// 0x92: Present(1), Ring(0), System(1), Executable(0), DirExpandDown(0), Writable(1), Accessed(0)
gdt_set_gate(2, 0, 0, 0x92, 0xAF);
// User Data segment (Ring 3)
// 0xF2: Present(1), Ring(3), System(1), Executable(0), DirExpandDown(0), Writable(1), Accessed(0)
gdt_set_gate(3, 0, 0, 0xF2, 0xAF);
// User Code segment (Ring 3, 64-bit)
// 0xFA: Present(1), Ring(3), System(1), Executable(1), DirConforming(0), Readable(1), Accessed(0)
// 0xAF: Long Mode (64-bit)
gdt_set_gate(4, 0, 0, 0xFA, 0xAF);
// TSS segment (takes entries 5 and 6 technically because it's 16 bytes)
gdt_memset(&tss, 0, sizeof(struct tss_entry));
tss.iopb_offset = sizeof(struct tss_entry);
// Allocate a default Ring 0 interrupt stack in case an interrupt fires early or
// the scheduler hasn't set one up yet for a task.
void* initial_tss_stack = kmalloc_aligned(4096, 4096);
if (initial_tss_stack) {
tss.rsp0 = (uint64_t)initial_tss_stack + 4096;
}
gdt_set_tss_gate(5, (uint64_t)&tss, sizeof(struct tss_entry) - 1, 0x89, 0x00);
gdt_flush((uint64_t)&gdtr);
tss_flush();
}

51
src/sys/gdt.h Normal file
View File

@@ -0,0 +1,51 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef GDT_H
#define GDT_H
#include <stdint.h>
// Segment Selectors
#define KERNEL_CS 0x08
#define KERNEL_DS 0x10
#define USER_CS 0x1B // 0x18 | 3 (RPL 3)
#define USER_DS 0x23 // 0x20 | 3 (RPL 3)
#define TSS_SEG 0x28
struct gdt_entry {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute__((packed));
struct tss_entry {
uint32_t reserved0;
uint64_t rsp0;
uint64_t rsp1;
uint64_t rsp2;
uint64_t reserved1;
uint64_t ist1;
uint64_t ist2;
uint64_t ist3;
uint64_t ist4;
uint64_t ist5;
uint64_t ist6;
uint64_t ist7;
uint64_t reserved2;
uint16_t reserved3;
uint16_t iopb_offset;
} __attribute__((packed));
struct gdt_ptr {
uint16_t limit;
uint64_t base;
} __attribute__((packed));
void gdt_init(void);
void tss_set_stack(uint64_t kernel_stack);
#endif

241
src/sys/idt.c Normal file
View File

@@ -0,0 +1,241 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "idt.h"
#include "io.h"
#include "kutils.h"
extern void serial_write(const char *str);
#include "process.h"
#include "cmd.h"
void kernel_panic(registers_t *regs, const char *error_name);
static const char *exception_messages[] = {
"Division By Zero",
"Debug",
"Non-Maskable Interrupt",
"Breakpoint",
"Into Detected Overflow",
"Out of Bounds",
"Invalid Opcode",
"No Coprocessor",
"Double Fault",
"Coprocessor Segment Overrun",
"Bad TSS",
"Segment Not Present",
"Stack Fault",
"General Protection Fault",
"Page Fault",
"Unknown Interrupt",
"Coprocessor Fault",
"Alignment Check",
"Machine Check",
"SIMD Floating-Point Exception",
"Virtualization Exception",
"Control Protection Exception",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Hypervisor Injection Exception",
"VMM Communication Exception",
"Security Exception",
"Reserved"
};
uint64_t exception_handler_c(registers_t *regs) {
uint64_t vector = regs->int_no;
char buf[17];
// Serial Mirror
serial_write("\n*** EXCEPTION ***\nVector: ");
k_itoa_hex(vector, buf);
serial_write("0x");
serial_write(buf);
if ((regs->cs & 0x3) != 0) {
serial_write("\nUSER MODE EXCEPTION - Terminating process.\n");
if (cmd_get_cursor_col() != 0) cmd_write("\n");
cmd_write("*** USER EXCEPTION ***\nVector: "); cmd_write_hex(vector);
cmd_write("\nRIP: "); cmd_write_hex(regs->rip);
cmd_write("\nTerminating process.\n");
return process_terminate_current();
}
// Kernel mode exception
const char *name = (vector < 32) ? exception_messages[vector] : "Unknown Kernel Exception";
kernel_panic(regs, name);
return (uint64_t)regs; // Unreachable
}
#define IDT_ENTRIES 256
struct idt_entry {
uint16_t isr_low;
uint16_t kernel_cs;
uint8_t ist;
uint8_t attributes;
uint16_t isr_mid;
uint32_t isr_high;
uint32_t reserved;
} __attribute__((packed));
struct idt_ptr {
uint16_t limit;
uint64_t base;
} __attribute__((packed));
static struct idt_entry idt[IDT_ENTRIES];
static struct idt_ptr idtr;
void idt_set_gate(uint8_t vector, void *isr, uint16_t cs, uint8_t flags) {
uint64_t addr = (uint64_t)isr;
idt[vector].isr_low = addr & 0xFFFF;
idt[vector].kernel_cs = cs;
idt[vector].ist = 0;
idt[vector].attributes = flags;
idt[vector].isr_mid = (addr >> 16) & 0xFFFF;
idt[vector].isr_high = (addr >> 32) & 0xFFFFFFFF;
idt[vector].reserved = 0;
}
// Remap PIC
static void pic_remap(void) {
uint8_t a1, a2;
a1 = inb(0x21);
a2 = inb(0xA1);
outb(0x20, 0x11); io_wait();
outb(0xA0, 0x11); io_wait();
outb(0x21, 0x20); io_wait(); // Master offset 0x20 (32)
outb(0xA1, 0x28); io_wait(); // Slave offset 0x28 (40)
outb(0x21, 0x04); io_wait();
outb(0xA1, 0x02); io_wait();
outb(0x21, 0x01); io_wait();
outb(0xA1, 0x01); io_wait();
// 0xEF = 1110 1111 (IRQ 12 (4 on slave) unmasked)
outb(0x21, 0xF9); // Unmask Keyboard (IRQ1) and Cascade (IRQ2)
outb(0xA1, 0xEF); // Unmask Mouse (IRQ12)
}
// Set up PIT (Programmable Interval Timer) for ~60Hz (16.67ms intervals)
static void pit_setup(void) {
uint16_t divisor = 1193182 / 60; // ~60Hz
// Send command byte
outb(0x43, 0x36); // Channel 0, lobyte/hibyte, mode 3 (square wave), binary
// Send divisor
outb(0x40, divisor & 0xFF);
outb(0x40, (divisor >> 8) & 0xFF);
}
void idt_init(void) {
uint16_t cs;
asm volatile ("mov %%cs, %0" : "=r"(cs));
for (int i = 0; i < IDT_ENTRIES; i++) {
idt[i] = (struct idt_entry){0};
}
pic_remap();
// Unmask IRQ 0 (Timer) in addition to IRQ 1 and 12
outb(0x21, 0xF8); // Unmask Timer (IRQ0), Keyboard (IRQ1) and Cascade (IRQ2)
outb(0xA1, 0xEF); // Unmask Mouse (IRQ12)
pit_setup();
}
void idt_register_interrupts(void) {
uint16_t cs;
asm volatile ("mov %%cs, %0" : "=r"(cs));
idt_set_gate(32, isr0_wrapper, cs, 0x8E); // Timer (IRQ 0)
idt_set_gate(33, isr1_wrapper, cs, 0x8E); // Keyboard (IRQ 1)
idt_set_gate(44, isr12_wrapper, cs, 0x8E); // Mouse (IRQ 12)
// Exceptions
extern void exc0_wrapper(void);
extern void exc1_wrapper(void);
extern void exc2_wrapper(void);
extern void exc3_wrapper(void);
extern void exc4_wrapper(void);
extern void exc5_wrapper(void);
extern void exc6_wrapper(void);
extern void exc7_wrapper(void);
extern void exc8_wrapper(void);
extern void exc9_wrapper(void);
extern void exc10_wrapper(void);
extern void exc11_wrapper(void);
extern void exc12_wrapper(void);
extern void exc13_wrapper(void);
extern void exc14_wrapper(void);
extern void exc15_wrapper(void);
extern void exc16_wrapper(void);
extern void exc17_wrapper(void);
extern void exc18_wrapper(void);
extern void exc19_wrapper(void);
extern void exc20_wrapper(void);
extern void exc21_wrapper(void);
extern void exc22_wrapper(void);
extern void exc23_wrapper(void);
extern void exc24_wrapper(void);
extern void exc25_wrapper(void);
extern void exc26_wrapper(void);
extern void exc27_wrapper(void);
extern void exc28_wrapper(void);
extern void exc29_wrapper(void);
extern void exc30_wrapper(void);
extern void exc31_wrapper(void);
idt_set_gate(0, exc0_wrapper, cs, 0x8E);
idt_set_gate(1, exc1_wrapper, cs, 0x8E);
idt_set_gate(2, exc2_wrapper, cs, 0x8E);
idt_set_gate(3, exc3_wrapper, cs, 0x8E);
idt_set_gate(4, exc4_wrapper, cs, 0x8E);
idt_set_gate(5, exc5_wrapper, cs, 0x8E);
idt_set_gate(6, exc6_wrapper, cs, 0x8E);
idt_set_gate(7, exc7_wrapper, cs, 0x8E);
idt_set_gate(8, exc8_wrapper, cs, 0x8E);
idt_set_gate(9, exc9_wrapper, cs, 0x8E);
idt_set_gate(10, exc10_wrapper, cs, 0x8E);
idt_set_gate(11, exc11_wrapper, cs, 0x8E);
idt_set_gate(12, exc12_wrapper, cs, 0x8E);
idt_set_gate(13, exc13_wrapper, cs, 0x8E);
idt_set_gate(14, exc14_wrapper, cs, 0x8E);
idt_set_gate(15, exc15_wrapper, cs, 0x8E);
idt_set_gate(16, exc16_wrapper, cs, 0x8E);
idt_set_gate(17, exc17_wrapper, cs, 0x8E);
idt_set_gate(18, exc18_wrapper, cs, 0x8E);
idt_set_gate(19, exc19_wrapper, cs, 0x8E);
idt_set_gate(20, exc20_wrapper, cs, 0x8E);
idt_set_gate(21, exc21_wrapper, cs, 0x8E);
idt_set_gate(22, exc22_wrapper, cs, 0x8E);
idt_set_gate(23, exc23_wrapper, cs, 0x8E);
idt_set_gate(24, exc24_wrapper, cs, 0x8E);
idt_set_gate(25, exc25_wrapper, cs, 0x8E);
idt_set_gate(26, exc26_wrapper, cs, 0x8E);
idt_set_gate(27, exc27_wrapper, cs, 0x8E);
idt_set_gate(28, exc28_wrapper, cs, 0x8E);
idt_set_gate(29, exc29_wrapper, cs, 0x8E);
idt_set_gate(30, exc30_wrapper, cs, 0x8E);
idt_set_gate(31, exc31_wrapper, cs, 0x8E);
}
void idt_load(void) {
idtr.base = (uint64_t)&idt;
idtr.limit = sizeof(struct idt_entry) * IDT_ENTRIES - 1;
asm volatile ("lidt %0" : : "m"(idtr));
// Do not sti here! The OS must decide when to enable interrupts
// after all subsystems (WM, PS/2) are initialized!
}

19
src/sys/idt.h Normal file
View File

@@ -0,0 +1,19 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
void idt_init(void);
void idt_set_gate(uint8_t vector, void *isr, uint16_t cs, uint8_t flags);
void idt_register_interrupts(void);
void idt_load(void);
// ISR wrappers defined in assembly
extern void isr0_wrapper(void); // Timer
extern void isr1_wrapper(void); // Keyboard
extern void isr12_wrapper(void); // Mouse
#endif

559
src/sys/process.c Normal file
View File

@@ -0,0 +1,559 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "process.h"
#include "gdt.h"
#include "idt.h"
#include "paging.h"
#include "io.h"
#include "platform.h"
#include "memory_manager.h"
#include "elf.h"
#include "wm.h"
extern void cmd_write(const char *str);
extern void serial_write(const char *str);
#define MAX_PROCESSES 16
process_t processes[MAX_PROCESSES] __attribute__((aligned(16)));
int process_count = 0;
static process_t* current_process = NULL;
static uint32_t next_pid = 0;
static void *free_kernel_stack_later = NULL;
void process_init(void) {
for (int i = 0; i < MAX_PROCESSES; i++) {
processes[i].pid = 0xFFFFFFFF;
}
// Current kernel execution is PID 0
process_t *kernel_proc = &processes[process_count++];
kernel_proc->pid = next_pid++;
kernel_proc->is_user = false;
// We don't have its RSP or PML4 yet, but it's already running.
// The timer interrupt will naturally capture its context on the first tick!
kernel_proc->pml4_phys = paging_get_pml4_phys();
kernel_proc->kernel_stack = 0;
// Initialize FPU/SSE state for kernel (first interrupt will capture it on stack)
kernel_proc->fpu_initialized = true;
for (int i = 0; i < MAX_PROCESS_FDS; i++) kernel_proc->fds[i] = NULL;
extern void mem_memcpy(void *dest, const void *src, size_t len);
mem_memcpy(kernel_proc->name, "kernel", 7);
kernel_proc->ticks = 0;
kernel_proc->used_memory = 32768; // Kernel stack
kernel_proc->next = kernel_proc; // Circular linked list
current_process = kernel_proc;
}
void process_create(void* entry_point, bool is_user) {
if (process_count >= MAX_PROCESSES) return;
process_t *new_proc = &processes[process_count++];
new_proc->pid = next_pid++;
new_proc->is_user = is_user;
// 1. Setup Page Table
if (is_user) {
new_proc->pml4_phys = paging_create_user_pml4_phys();
} else {
new_proc->pml4_phys = paging_get_pml4_phys();
}
if (!new_proc->pml4_phys) return;
// 2. Allocate aligned stack
void* user_stack = kmalloc_aligned(4096, 4096);
void* kernel_stack = kmalloc_aligned(32768, 32768); // Needed for when user interrupts to Ring 0
if (is_user) {
// Map user stack to 0x800000
paging_map_page(new_proc->pml4_phys, 0x800000, v2p((uint64_t)user_stack), PT_PRESENT | PT_RW | PT_USER);
// Allocate code page aligned and copy code
void* code = kmalloc_aligned(4096, 4096);
for(int i=0; i<128; i++) ((uint8_t*)code)[i] = ((uint8_t*)entry_point)[i];
paging_map_page(new_proc->pml4_phys, 0x400000, v2p((uint64_t)code), PT_PRESENT | PT_RW | PT_USER);
// Build initial stack frame for iretq
// Stack grows down, start at top
uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 32768);
*(--stack_ptr) = 0x1B; // SS (User Data)
*(--stack_ptr) = 0x800000 + 4096; // RSP
*(--stack_ptr) = 0x202; // RFLAGS (IF=1)
*(--stack_ptr) = 0x23; // CS (User Code)
*(--stack_ptr) = 0x400000; // RIP
*(--stack_ptr) = 0; // int_no
*(--stack_ptr) = 0; // err_code
// Push 15 zeros for general purpose registers (r15 -> rax)
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
// Push 512 bytes for SSE/FPU state (fxsave_region)
// Zero it out for safety
stack_ptr = (uint64_t*)((uint64_t)stack_ptr - 512);
for (int i = 0; i < 512/8; i++) stack_ptr[i] = 0;
new_proc->kernel_stack = (uint64_t)kernel_stack + 32768;
new_proc->rsp = (uint64_t)stack_ptr;
} else {
// Kernel thread
uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 32768);
*(--stack_ptr) = 0x10; // SS (Kernel Data)
stack_ptr--;
*stack_ptr = (uint64_t)stack_ptr; // RSP
*(--stack_ptr) = 0x202; // RFLAGS
*(--stack_ptr) = 0x08; // CS (Kernel Code)
*(--stack_ptr) = (uint64_t)entry_point; // RIP
*(--stack_ptr) = 0; // int_no
*(--stack_ptr) = 0; // err_code
// Push 15 zeros for general purpose registers (r15 -> rax)
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
// Push 512 bytes for SSE/FPU state (fxsave_region)
stack_ptr = (uint64_t*)((uint64_t)stack_ptr - 512);
// Zero it out for safety
for (int i = 0; i < 512/8; i++) stack_ptr[i] = 0;
new_proc->kernel_stack = (uint64_t)kernel_stack + 32768;
new_proc->rsp = (uint64_t)stack_ptr;
kfree(user_stack); // Unused for kernel threads
}
// Initialize FPU state for new process
asm volatile("fninit");
new_proc->fpu_initialized = true;
// Add to linked list (Critical Section)
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
new_proc->next = current_process->next;
current_process->next = new_proc;
asm volatile("push %0; popfq" : : "r"(rflags));
}
process_t* process_create_elf(const char* filepath, const char* args_str) {
process_t *new_proc = NULL;
// Find an available slot
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == 0xFFFFFFFF || i >= process_count) {
new_proc = &processes[i];
if (i >= process_count) process_count = i + 1;
break;
}
}
if (!new_proc) return NULL;
new_proc->pid = next_pid++;
new_proc->is_user = true;
// 1. Setup Page Table
new_proc->pml4_phys = paging_create_user_pml4_phys();
if (!new_proc->pml4_phys) return NULL;
for (int i = 0; i < MAX_PROCESS_FDS; i++) new_proc->fds[i] = NULL;
new_proc->gui_event_head = 0;
new_proc->gui_event_tail = 0;
new_proc->ui_window = NULL;
new_proc->heap_start = 0x20000000; // 512MB mark
new_proc->heap_end = 0x20000000;
new_proc->is_terminal_proc = false;
// 2. Load ELF executable
size_t elf_load_size = 0;
uint64_t entry_point = elf_load(filepath, new_proc->pml4_phys, &elf_load_size);
if (entry_point == 0) {
serial_write("[PROCESS] Failed to load ELF: ");
serial_write(filepath);
serial_write("\n");
// We technically leak the page table here, but let's ignore cleanup for now
return NULL;
}
// Set process name from filepath
int last_slash = -1;
for (int i = 0; filepath[i]; i++) if (filepath[i] == '/') last_slash = i;
const char *filename = (last_slash == -1) ? filepath : (filepath + last_slash + 1);
int ni = 0;
while (filename[ni] && ni < 63) {
new_proc->name[ni] = filename[ni];
ni++;
}
new_proc->name[ni] = 0;
new_proc->ticks = 0;
// 3. Allocate generic User stack and Kernel stack for interrupts
// Increase to 256KB to prevent stack smashing on heavy networking
size_t user_stack_size = 262144;
void* stack = kmalloc_aligned(user_stack_size, 4096);
void* kernel_stack = kmalloc_aligned(65536, 65536);
// Map User stack to 0x800000
for (uint64_t i = 0; i < (user_stack_size / 4096); i++) {
paging_map_page(new_proc->pml4_phys, 0x800000 - user_stack_size + (i * 4096), v2p((uint64_t)stack + (i * 4096)), PT_PRESENT | PT_RW | PT_USER);
}
int argc = 1;
char *args_buf = (char *)stack + user_stack_size;
uint64_t user_args_buf = 0x800000;
// Copy filepath as argv[0]
int path_len = 0;
while (filepath[path_len]) path_len++;
args_buf -= (path_len + 1);
user_args_buf -= (path_len + 1);
for (int i = 0; i <= path_len; i++) args_buf[i] = filepath[i];
uint64_t argv_ptrs[32];
argv_ptrs[0] = user_args_buf;
if (args_str) {
int i = 0;
while (args_str[i] && argc < 31) {
// Skip spaces
while (args_str[i] == ' ') i++;
if (!args_str[i]) break;
int arg_start = i;
bool in_quotes = false;
if (args_str[i] == '"') {
in_quotes = true;
i++;
arg_start = i;
while (args_str[i] && args_str[i] != '"') i++;
} else {
while (args_str[i] && args_str[i] != ' ') i++;
}
int arg_len = i - arg_start;
args_buf -= (arg_len + 1);
user_args_buf -= (arg_len + 1);
for (int k = 0; k < arg_len; k++) {
args_buf[k] = args_str[arg_start + k];
}
args_buf[arg_len] = '\0';
argv_ptrs[argc++] = user_args_buf;
if (in_quotes && args_str[i] == '"') i++; // Skip closing quote
}
}
argv_ptrs[argc] = 0; // Null terminator for argv
// Align stack to 8 bytes before pushing argv array
uint64_t current_user_sp = user_args_buf;
current_user_sp &= ~7ULL;
args_buf = (char *)((uint64_t)stack + (current_user_sp - (0x800000 - user_stack_size)));
// Push argv array
int argv_size = (argc + 1) * sizeof(uint64_t);
args_buf -= argv_size;
current_user_sp -= argv_size;
uint64_t actual_argv_ptr = current_user_sp; // Store the true pointer to argv array
uint64_t *user_argv_array = (uint64_t *)args_buf;
for (int i = 0; i <= argc; i++) {
user_argv_array[i] = argv_ptrs[i];
}
// Align stack to 16 bytes. crt0.asm does `and rsp, -16`, but it's good practice
current_user_sp &= ~15ULL;
// 4. Build Stack Frame for context switch via IRETQ
uint64_t* stack_ptr = (uint64_t*)((uint64_t)kernel_stack + 65536);
*(--stack_ptr) = 0x1B; // SS (User Mode Data)
*(--stack_ptr) = current_user_sp; // RSP (Updated user stack pointer)
*(--stack_ptr) = 0x202; // RFLAGS (Interrupts Enabled)
*(--stack_ptr) = 0x23; // CS (User Mode Code)
*(--stack_ptr) = entry_point; // RIP
*(--stack_ptr) = 0; // err_code
*(--stack_ptr) = 0; // int_no
// 15 General purpose registers
*(--stack_ptr) = 0; // RAX
*(--stack_ptr) = 0; // RBX
*(--stack_ptr) = 0; // RCX
*(--stack_ptr) = 0; // RDX
*(--stack_ptr) = actual_argv_ptr; // RSI = actual argv array
*(--stack_ptr) = argc; // RDI = argc
*(--stack_ptr) = 0; // RBP
*(--stack_ptr) = 0; // R8
*(--stack_ptr) = 0; // R9
*(--stack_ptr) = 0; // R10
*(--stack_ptr) = 0; // R11
*(--stack_ptr) = 0; // R12
*(--stack_ptr) = 0; // R13
*(--stack_ptr) = 0; // R14
*(--stack_ptr) = 0; // R15
// Space for 512-byte fxsave_region
stack_ptr = (uint64_t*)((uint64_t)stack_ptr - 512);
// Initialize with a clean FPU state
asm volatile("fninit");
asm volatile("fxsave %0" : "=m"(*stack_ptr));
new_proc->kernel_stack = (uint64_t)kernel_stack + 65536;
new_proc->kernel_stack_alloc = kernel_stack;
new_proc->user_stack_alloc = stack;
new_proc->rsp = (uint64_t)stack_ptr;
new_proc->used_memory = elf_load_size + user_stack_size + 65536;
// Initialize FPU state for new process
asm volatile("fninit");
new_proc->fpu_initialized = true;
// Add to linked list (Critical Section)
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
new_proc->next = current_process->next;
current_process->next = new_proc;
asm volatile("push %0; popfq" : : "r"(rflags));
serial_write("[PROCESS] Spawned ELF Executable: ");
serial_write(filepath);
serial_write("\n");
return new_proc;
}
process_t* process_get_current(void) {
return current_process;
}
uint64_t process_schedule(uint64_t current_rsp) {
if (free_kernel_stack_later) {
kfree(free_kernel_stack_later);
free_kernel_stack_later = NULL;
}
if (!current_process || !current_process->next || current_process == current_process->next)
return current_rsp;
// Save context
current_process->rsp = current_rsp;
// Switch to next ready process
extern uint32_t wm_get_ticks(void);
uint32_t now = wm_get_ticks();
process_t *start = current_process;
process_t *next_proc = current_process->next;
while (next_proc != start) {
if (next_proc->pid == 0 || next_proc->sleep_until == 0 || next_proc->sleep_until <= now) {
break;
}
next_proc = next_proc->next;
}
current_process = next_proc;
// Update Kernel Stack for User Mode interrupts and System Calls
if (current_process->is_user && current_process->kernel_stack) {
tss_set_stack(current_process->kernel_stack);
extern uint64_t kernel_syscall_stack;
kernel_syscall_stack = current_process->kernel_stack;
}
// Switch page table
paging_switch_directory(current_process->pml4_phys);
current_process->ticks++;
return current_process->rsp;
}
process_t* process_get_by_pid(uint32_t pid) {
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid == pid) return &processes[i];
}
return NULL;
}
static void process_cleanup_inner(process_t *proc) {
if (!proc || proc->pid == 0xFFFFFFFF) return;
// 1. Cleanup side effects
extern Window win_cmd;
if (proc->ui_window && (proc->ui_window != &win_cmd)) {
wm_remove_window((Window *)proc->ui_window);
proc->ui_window = NULL;
}
extern void fat32_close(struct FAT32_FileHandle *fh);
for (int i = 0; i < MAX_PROCESS_FDS; i++) {
if (proc->fds[i]) {
fat32_close(proc->fds[i]);
proc->fds[i] = NULL;
}
}
extern void cmd_process_finished(void);
cmd_process_finished();
extern void network_cleanup_pcb(void *pcb);
// TODO: We need per-process PCB tracking to call this safely
// For now, let's NOT call global network_cleanup
}
void process_terminate(process_t *to_delete) {
if (!to_delete || to_delete->pid == 0xFFFFFFFF || to_delete->pid == 0) return;
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
process_cleanup_inner(to_delete);
// 2. Find previous process in circular list
process_t *prev = to_delete;
while (prev->next != to_delete) {
prev = prev->next;
}
if (prev == to_delete) {
// Only one process (should be kernel), cannot terminate.
asm volatile("push %0; popfq" : : "r"(rflags));
return;
}
// 3. Remove current from list
prev->next = to_delete->next;
if (to_delete == current_process) {
current_process = to_delete->next;
// WARNING: If this was called as a regular function and not via a task switch,
// the stack might be in a weird state. But usually we call this via window manager
// or other external triggers.
}
// Mark slot as free
to_delete->pid = 0xFFFFFFFF;
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
if (to_delete->kernel_stack_alloc) {
if (to_delete == current_process) {
free_kernel_stack_later = to_delete->kernel_stack_alloc;
} else {
kfree(to_delete->kernel_stack_alloc);
}
}
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
if (to_delete->pml4_phys && to_delete->is_user) {
paging_destroy_user_pml4_phys(to_delete->pml4_phys);
}
to_delete->user_stack_alloc = NULL;
to_delete->kernel_stack_alloc = NULL;
to_delete->pml4_phys = 0;
asm volatile("push %0; popfq" : : "r"(rflags));
}
uint64_t process_terminate_current(void) {
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
if (!current_process || current_process->pid == 0) {
asm volatile("push %0; popfq" : : "r"(rflags));
return 0;
}
process_cleanup_inner(current_process);
// 2. Find previous process in circular list
process_t *prev = current_process;
while (prev->next != current_process) {
prev = prev->next;
}
// 3. Remove current from list
process_t *to_delete = current_process;
if (prev == current_process) {
// Only one process (should be kernel), cannot terminate.
asm volatile("push %0; popfq" : : "r"(rflags));
return to_delete->rsp;
}
prev->next = to_delete->next;
current_process = to_delete->next;
// Mark slot as free
to_delete->pid = 0xFFFFFFFF;
// 4. Load context for the NEXT process
if (current_process->is_user && current_process->kernel_stack) {
tss_set_stack(current_process->kernel_stack);
extern uint64_t kernel_syscall_stack;
kernel_syscall_stack = current_process->kernel_stack;
}
paging_switch_directory(current_process->pml4_phys);
// 5. Actually free the memory (after switching state to avoid issues)
// We only safely free the user stack. Immediate freeing of the current
// kernel stack is unsafe while we are still running on it.
if (to_delete->user_stack_alloc) kfree(to_delete->user_stack_alloc);
extern void paging_destroy_user_pml4_phys(uint64_t pml4_phys);
if (to_delete->pml4_phys && to_delete->is_user) {
paging_destroy_user_pml4_phys(to_delete->pml4_phys);
}
// Clear pointers to avoid double-free during slot reuse
to_delete->user_stack_alloc = NULL;
free_kernel_stack_later = to_delete->kernel_stack_alloc;
to_delete->kernel_stack_alloc = NULL; // Leak the small kernel stack for safety
to_delete->pml4_phys = 0;
uint64_t next_rsp = current_process->rsp;
asm volatile("push %0; popfq" : : "r"(rflags));
return next_rsp;
}
void process_push_gui_event(process_t *proc, gui_event_t *ev) {
if (!proc) return;
// Coalesce PAINT events: if a PAINT event is already in the queue, don't add another
if (ev->type == 1) { // GUI_EVENT_PAINT
int curr = proc->gui_event_head;
while (curr != proc->gui_event_tail) {
if (proc->gui_events[curr].type == 1) {
return; // Already has a paint event pending
}
curr = (curr + 1) % MAX_GUI_EVENTS;
}
}
int next_tail = (proc->gui_event_tail + 1) % MAX_GUI_EVENTS;
// Drop event if queue is full
if (next_tail == proc->gui_event_head) {
extern void serial_write(const char *str);
return;
}
proc->gui_events[proc->gui_event_tail] = *ev;
proc->gui_event_tail = next_tail;
}
process_t* process_get_by_ui_window(void *win) {
for (int i = 0; i < MAX_PROCESSES; i++) {
if (processes[i].pid != 0xFFFFFFFF && processes[i].ui_window == win) {
return &processes[i];
}
}
return NULL;
}

77
src/sys/process.h Normal file
View File

@@ -0,0 +1,77 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef PROCESS_H
#define PROCESS_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "gui_ipc.h"
#define MAX_GUI_EVENTS 32
#define MAX_PROCESS_FDS 16
struct FAT32_FileHandle;
typedef struct registers_t {
uint8_t fxsave_region[512];
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
uint64_t rbp, rdi, rsi, rdx, rcx, rbx, rax;
uint64_t int_no, err_code;
uint64_t rip, cs, rflags, rsp, ss;
} __attribute__((packed, aligned(16))) registers_t;
typedef struct process {
uint32_t pid;
uint64_t rsp;
uint64_t pml4_phys;
uint64_t kernel_stack;
bool is_user;
gui_event_t gui_events[MAX_GUI_EVENTS];
int gui_event_head;
int gui_event_tail;
void *ui_window;
uint64_t heap_start;
uint64_t heap_end;
void *fds[MAX_PROCESS_FDS];
void *kernel_stack_alloc;
void *user_stack_alloc;
bool is_terminal_proc;
struct process *next;
bool fpu_initialized;
char name[64];
uint64_t ticks;
uint64_t sleep_until;
size_t used_memory;
} __attribute__((aligned(16))) process_t;
typedef struct {
uint32_t pid;
char name[64];
uint64_t ticks;
size_t used_memory;
} ProcessInfo;
void process_init(void);
void process_create(void* entry_point, bool is_user);
process_t* process_create_elf(const char* filepath, const char* args_str);
process_t* process_get_current(void);
uint64_t process_schedule(uint64_t current_rsp);
uint64_t process_terminate_current(void);
void process_terminate(process_t *proc);
process_t* process_get_by_pid(uint32_t pid);
void process_push_gui_event(process_t *proc, gui_event_t *ev);
process_t* process_get_by_ui_window(void* win);
#endif

1249
src/sys/syscall.c Normal file

File diff suppressed because it is too large Load Diff

58
src/sys/syscall.h Normal file
View File

@@ -0,0 +1,58 @@
// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#ifndef SYSCALL_H
#define SYSCALL_H
#include <stdint.h>
// Forward declarations
typedef struct Window Window;
typedef struct registers_t registers_t;
// MSRs used for syscalls in x86_64
#define MSR_EFER 0xC0000080
#define MSR_STAR 0xC0000081
#define MSR_LSTAR 0xC0000082
#define MSR_COMPAT_STAR 0xC0000083
#define MSR_FMASK 0xC0000084
// Syscall Numbers
#define SYS_WRITE 1
#define SYS_GUI 3
#define SYS_FS 4
#define SYS_EXIT 60
// FS Commands
#define FS_CMD_OPEN 1
#define FS_CMD_READ 2
#define FS_CMD_WRITE 3
#define FS_CMD_CLOSE 4
#define FS_CMD_SEEK 5
#define FS_CMD_TELL 6
#define FS_CMD_LIST 7
#define FS_CMD_DELETE 8
#define FS_CMD_SIZE 9
#define FS_CMD_MKDIR 10
#define FS_CMD_EXISTS 11
#define FS_CMD_GETCWD 12
#define FS_CMD_CHDIR 13
#define FS_CMD_GET_INFO 14
#define SYSTEM_CMD_SET_RAW_MODE 41
#define SYSTEM_CMD_TCP_RECV_NB 42
#define SYSTEM_CMD_YIELD 43
#define SYSTEM_CMD_PROCESS_LIST 44
#define SYSTEM_CMD_GET_CPU_MODEL 45
#define SYSTEM_CMD_SLEEP 46
#define SYSTEM_CMD_SET_RESOLUTION 47
void syscall_init(void);
uint64_t syscall_handler_c(registers_t *regs);
// Mouse event helpers for WM
void syscall_send_mouse_move_event(Window *win, int x, int y, uint8_t buttons);
void syscall_send_mouse_down_event(Window *win, int x, int y);
void syscall_send_mouse_up_event(Window *win, int x, int y);
#endif // SYSCALL_H