mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 10:26:59 +00:00
src/kernel --> src/
This commit is contained in:
116
src/sys/elf.c
Normal file
116
src/sys/elf.c
Normal 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
78
src/sys/elf.h
Normal 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
108
src/sys/gdt.c
Normal 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
51
src/sys/gdt.h
Normal 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
241
src/sys/idt.c
Normal 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
19
src/sys/idt.h
Normal 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
559
src/sys/process.c
Normal 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
77
src/sys/process.h
Normal 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
1249
src/sys/syscall.c
Normal file
File diff suppressed because it is too large
Load Diff
58
src/sys/syscall.h
Normal file
58
src/sys/syscall.h
Normal 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
|
||||
Reference in New Issue
Block a user