mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
Ring 3 multitasking
This commit is contained in:
parent
cc950974e8
commit
ca997072ce
49 changed files with 70045 additions and 41 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
15
Makefile
15
Makefile
|
|
@ -70,6 +70,16 @@ $(BUILD_DIR)/cli_apps/%.o: $(SRC_DIR)/cli_apps/%.c | $(BUILD_DIR) limine-setup
|
|||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.asm | $(BUILD_DIR)
|
||||
$(NASM) $(NASMFLAGS) $< -o $@
|
||||
|
||||
# Assemble test files specifically if they get missed
|
||||
$(BUILD_DIR)/test_syscall.o: $(SRC_DIR)/test_syscall.asm | $(BUILD_DIR)
|
||||
$(NASM) $(NASMFLAGS) $< -o $@
|
||||
|
||||
$(BUILD_DIR)/user_test.o: $(SRC_DIR)/user_test.asm | $(BUILD_DIR)
|
||||
$(NASM) $(NASMFLAGS) $< -o $@
|
||||
|
||||
$(BUILD_DIR)/process_asm.o: $(SRC_DIR)/process_asm.asm | $(BUILD_DIR)
|
||||
$(NASM) $(NASMFLAGS) $< -o $@
|
||||
|
||||
# Link Kernel
|
||||
$(KERNEL_ELF): $(OBJ_FILES)
|
||||
$(LD) $(LDFLAGS) -o $@ $(OBJ_FILES)
|
||||
|
|
@ -116,8 +126,9 @@ clean:
|
|||
rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE)
|
||||
|
||||
run: $(ISO_IMAGE)
|
||||
qemu-system-x86_64 -m 2G -serial stdio -cdrom $(ISO_IMAGE) -boot d \
|
||||
qemu-system-x86_64 -m 2G -serial stdio -cdrom $< -boot d \
|
||||
-audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \
|
||||
-netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 \
|
||||
-vga std -global VGA.xres=1920 -global VGA.yres=1080 \
|
||||
-drive file=disk.img,format=raw
|
||||
-drive file=disk.img,format=raw \
|
||||
-no-reboot -d int -D qemu-debug.log
|
||||
|
|
|
|||
1
addr2line.log
Normal file
1
addr2line.log
Normal file
|
|
@ -0,0 +1 @@
|
|||
??:0
|
||||
50772
boredos.dump
Normal file
50772
boredos.dump
Normal file
File diff suppressed because it is too large
Load diff
BIN
boredos.iso
BIN
boredos.iso
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/cmd.o
BIN
build/cmd.o
Binary file not shown.
Binary file not shown.
BIN
build/explorer.o
BIN
build/explorer.o
Binary file not shown.
BIN
build/fat32.o
BIN
build/fat32.o
Binary file not shown.
BIN
build/idt.o
BIN
build/idt.o
Binary file not shown.
Binary file not shown.
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/ps2.o
BIN
build/ps2.o
Binary file not shown.
BIN
build/rtc.o
BIN
build/rtc.o
Binary file not shown.
BIN
build/tcp.o
BIN
build/tcp.o
Binary file not shown.
BIN
build/viewer.o
BIN
build/viewer.o
Binary file not shown.
BIN
build/vm.o
BIN
build/vm.o
Binary file not shown.
Binary file not shown.
BIN
build/wm.o
BIN
build/wm.o
Binary file not shown.
Binary file not shown.
18286
qemu-debug.log
Normal file
18286
qemu-debug.log
Normal file
File diff suppressed because it is too large
Load diff
105
src/kernel/gdt.c
Normal file
105
src/kernel/gdt.c
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#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();
|
||||
}
|
||||
48
src/kernel/gdt.h
Normal file
48
src/kernel/gdt.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#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
|
||||
28
src/kernel/gdt_asm.asm
Normal file
28
src/kernel/gdt_asm.asm
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
global gdt_flush
|
||||
global tss_flush
|
||||
|
||||
section .text
|
||||
|
||||
gdt_flush:
|
||||
lgdt [rdi] ; Load GDT from the pointer passed in RDI
|
||||
|
||||
mov ax, 0x10 ; 0x10 is our offset in the GDT to our data segment
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
; Far jump to update CS
|
||||
push 0x08 ; 0x08 is our offset to the code segment
|
||||
lea rax, [rel .flush]
|
||||
push rax
|
||||
retfq
|
||||
|
||||
.flush:
|
||||
ret
|
||||
|
||||
tss_flush:
|
||||
mov ax, 0x28 ; 0x28 is our offset in the GDT to the TSS
|
||||
ltr ax
|
||||
ret
|
||||
|
|
@ -1,6 +1,34 @@
|
|||
#include "idt.h"
|
||||
#include "io.h"
|
||||
|
||||
extern void serial_write(const char *str);
|
||||
|
||||
// Simple hex printer for debugging exceptions
|
||||
static void print_hex(uint64_t val) {
|
||||
const char* digits = "0123456789ABCDEF";
|
||||
char buf[17];
|
||||
buf[16] = '\0';
|
||||
for (int i = 15; i >= 0; i--) {
|
||||
buf[i] = digits[val & 0xF];
|
||||
val >>= 4;
|
||||
}
|
||||
serial_write("0x");
|
||||
serial_write(buf);
|
||||
}
|
||||
|
||||
void exception_handler_c(uint64_t vector, uint64_t err_code, uint64_t rip, uint64_t cr2) {
|
||||
serial_write("\n*** EXCEPTION ***\nVector: ");
|
||||
print_hex(vector);
|
||||
serial_write("\nError Code: ");
|
||||
print_hex(err_code);
|
||||
serial_write("\nRIP: ");
|
||||
print_hex(rip);
|
||||
serial_write("\nCR2: ");
|
||||
print_hex(cr2);
|
||||
serial_write("\nCPU HALTED.\n");
|
||||
while(1) { asm volatile("cli; hlt"); }
|
||||
}
|
||||
|
||||
#define IDT_ENTRIES 256
|
||||
|
||||
struct idt_entry {
|
||||
|
|
@ -89,11 +117,18 @@ void idt_register_interrupts(void) {
|
|||
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 isr8_wrapper(void);
|
||||
extern void isr14_wrapper(void);
|
||||
idt_set_gate(8, isr8_wrapper, cs, 0x8E); // Double Fault
|
||||
idt_set_gate(14, isr14_wrapper, cs, 0x8E); // Page Fault
|
||||
}
|
||||
|
||||
void idt_load(void) {
|
||||
idtr.base = (uint64_t)&idt;
|
||||
idtr.limit = sizeof(struct idt_entry) * IDT_ENTRIES - 1;
|
||||
asm volatile ("lidt %0" : : "m"(idtr));
|
||||
asm volatile ("sti");
|
||||
// Do not sti here! The OS must decide when to enable interrupts
|
||||
// after all subsystems (WM, PS/2) are initialized!
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
section .text
|
||||
global isr0_wrapper
|
||||
global isr1_wrapper
|
||||
global isr8_wrapper
|
||||
global isr12_wrapper
|
||||
global isr14_wrapper
|
||||
extern timer_handler
|
||||
extern keyboard_handler
|
||||
extern mouse_handler
|
||||
extern exception_handler_c
|
||||
|
||||
; Helper to send EOI (End of Interrupt) to PIC
|
||||
send_eoi:
|
||||
|
|
@ -16,15 +19,15 @@ send_eoi:
|
|||
ret
|
||||
|
||||
%macro ISR_NOERRCODE 1
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
push rcx
|
||||
push r8
|
||||
push r9
|
||||
push rax
|
||||
push rbx
|
||||
push rcx
|
||||
push rdx
|
||||
push rbp
|
||||
push rdi
|
||||
push rsi
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push r12
|
||||
|
|
@ -32,23 +35,29 @@ send_eoi:
|
|||
push r14
|
||||
push r15
|
||||
|
||||
; Pass current RSP as 1st argument
|
||||
mov rdi, rsp
|
||||
|
||||
call %1
|
||||
|
||||
; Update RSP with return value (task switch)
|
||||
mov rsp, rax
|
||||
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop r11
|
||||
pop r10
|
||||
pop rbp
|
||||
pop rbx
|
||||
pop rax
|
||||
pop r9
|
||||
pop r8
|
||||
pop rcx
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rbp
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rbx
|
||||
pop rax
|
||||
iretq
|
||||
%endmacro
|
||||
|
||||
|
|
@ -60,3 +69,62 @@ isr1_wrapper:
|
|||
|
||||
isr12_wrapper:
|
||||
ISR_NOERRCODE mouse_handler
|
||||
|
||||
; Common exception macro
|
||||
%macro EXCEPTION_ERRCODE 1
|
||||
isr%1_wrapper:
|
||||
push %1
|
||||
jmp exception_common
|
||||
%endmacro
|
||||
|
||||
; Exception 8: Double Fault (has error code)
|
||||
EXCEPTION_ERRCODE 8
|
||||
|
||||
; Exception 14: Page Fault (has error code)
|
||||
EXCEPTION_ERRCODE 14
|
||||
|
||||
exception_common:
|
||||
; Save registers
|
||||
push rax
|
||||
push rcx
|
||||
push rdx
|
||||
push rbx
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
; Call C handler: void exception_handler_c(uint64_t vector, uint64_t err_code, uint64_t rip, uint64_t cr2)
|
||||
; Stack right now: 15 registers (15*8=120 bytes), then vector (8), then err_code (8), then RIP (8), CS (8), RFLAGS (8), RSP (8), SS (8)
|
||||
mov rdi, [rsp + 120] ; vector
|
||||
mov rsi, [rsp + 128] ; err_code
|
||||
mov rdx, [rsp + 136] ; RIP
|
||||
mov rcx, cr2 ; CR2
|
||||
|
||||
call exception_handler_c
|
||||
|
||||
; Restore (in case we want to return, but usually we halt)
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rbx
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rax
|
||||
add rsp, 16 ; drop vector and error code
|
||||
iretq
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@
|
|||
#include <stdbool.h>
|
||||
#include "limine.h"
|
||||
#include "graphics.h"
|
||||
#include "gdt.h"
|
||||
#include "idt.h"
|
||||
#include "paging.h"
|
||||
#include "syscall.h"
|
||||
#include "process.h"
|
||||
#include "ps2.h"
|
||||
#include "wm.h"
|
||||
#include "io.h"
|
||||
|
|
@ -47,27 +51,81 @@ static void hcf(void) {
|
|||
}
|
||||
}
|
||||
|
||||
// Simple serial port initialization and output for debugging
|
||||
static void init_serial() {
|
||||
outb(0x3F8 + 1, 0x00);
|
||||
outb(0x3F8 + 3, 0x80);
|
||||
outb(0x3F8 + 0, 0x03);
|
||||
outb(0x3F8 + 1, 0x00);
|
||||
outb(0x3F8 + 3, 0x03);
|
||||
outb(0x3F8 + 2, 0xC7);
|
||||
outb(0x3F8 + 4, 0x0B);
|
||||
}
|
||||
|
||||
void serial_write(const char *str) {
|
||||
while (*str) {
|
||||
while ((inb(0x3F8 + 5) & 0x20) == 0);
|
||||
outb(0x3F8, *str++);
|
||||
}
|
||||
}
|
||||
|
||||
// Kernel Entry Point
|
||||
void kmain(void) {
|
||||
platform_init();
|
||||
// 1. Graphics Init
|
||||
init_serial();
|
||||
serial_write("\n[DEBUG] Entering kmain...\n");
|
||||
|
||||
platform_init();
|
||||
serial_write("[DEBUG] platform_init OK\n");
|
||||
|
||||
// 1. Graphics Init
|
||||
if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) {
|
||||
serial_write("[DEBUG] No framebuffer! Halting.\n");
|
||||
hcf();
|
||||
}
|
||||
|
||||
struct limine_framebuffer *fb = framebuffer_request.response->framebuffers[0];
|
||||
graphics_init(fb);
|
||||
serial_write("[DEBUG] graphics_init OK\n");
|
||||
|
||||
// 1.5 GDT & TSS Init
|
||||
gdt_init();
|
||||
serial_write("[DEBUG] gdt_init OK\n");
|
||||
|
||||
// 1.6 Paging Init
|
||||
paging_init();
|
||||
serial_write("[DEBUG] paging_init OK\n");
|
||||
|
||||
// 1.7 Syscall Init
|
||||
syscall_init();
|
||||
serial_write("[DEBUG] syscall_init OK\n");
|
||||
|
||||
// Set up a user page and jump to user space
|
||||
// 2. Interrupts Init
|
||||
idt_init();
|
||||
|
||||
// Register ISRs with correct CS
|
||||
idt_register_interrupts();
|
||||
|
||||
// Load IDT and Enable Interrupts
|
||||
idt_load();
|
||||
serial_write("[DEBUG] idt_init OK\n");
|
||||
|
||||
process_init();
|
||||
int ENABLE_USER_TEST = 1; // Set to 1 to test User Mode ring 3 jump
|
||||
if (ENABLE_USER_TEST) {
|
||||
// Create an isolated PML4 table for this "process"
|
||||
uint64_t user_pml4_phys = paging_create_user_pml4_phys();
|
||||
serial_write("[DEBUG] user_pml4 created OK\n");
|
||||
if (user_pml4_phys) {
|
||||
|
||||
// Debug verify we can allocate
|
||||
void* code_page = kmalloc_aligned(4096, 4096);
|
||||
if (code_page) {
|
||||
extern void user_test_function(void);
|
||||
process_create(user_test_function, true);
|
||||
serial_write("[DEBUG] User Process 1 Created.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serial_write("[DEBUG] Skipping user mode test, proceeding with normal boot.\n");
|
||||
|
||||
// 2.5 Memory Manager Init - Calculate available RAM from Limine
|
||||
size_t total_usable_memory = 0;
|
||||
if (memmap_request.response != NULL) {
|
||||
|
|
@ -99,6 +157,9 @@ void kmain(void) {
|
|||
// 4. Window Manager Init (Draws initial desktop)
|
||||
wm_init();
|
||||
|
||||
// Re-enable interrupts since we removed sti from idt_load
|
||||
asm volatile("sti");
|
||||
|
||||
// 5. Main loop - just wait for interrupts
|
||||
// Timer interrupt will drive the redraw system
|
||||
while (1) {
|
||||
|
|
|
|||
|
|
@ -44,11 +44,22 @@ static uint32_t get_timestamp(void) {
|
|||
return tick++;
|
||||
}
|
||||
|
||||
// Find free space in memory pool
|
||||
static void* find_free_space(size_t size) {
|
||||
// Find free space in memory pool with alignment
|
||||
static void* find_free_space_aligned(size_t size, size_t alignment) {
|
||||
size_t offset = 0;
|
||||
|
||||
// Ensure 8-byte minimum alignment for regular malloc if 0 is passed
|
||||
if (alignment == 0) alignment = 8;
|
||||
|
||||
while (offset + size <= memory_pool_size) {
|
||||
// Align offset
|
||||
if ((uint64_t)((uint8_t*)memory_pool + offset) % alignment != 0) {
|
||||
size_t diff = alignment - ((uint64_t)((uint8_t*)memory_pool + offset) % alignment);
|
||||
offset += diff;
|
||||
}
|
||||
|
||||
if (offset + size > memory_pool_size) break;
|
||||
|
||||
bool space_free = true;
|
||||
|
||||
// Check if this range is free
|
||||
|
|
@ -63,12 +74,8 @@ static void* find_free_space(size_t size) {
|
|||
// Check for overlap
|
||||
if (check_start < block_end && check_end > block_start) {
|
||||
space_free = false;
|
||||
|
||||
// Skip past this block
|
||||
size_t block_offset = (uintptr_t)block_end - (uintptr_t)memory_pool;
|
||||
if (block_offset > offset) {
|
||||
offset = block_offset;
|
||||
}
|
||||
// Move offset past this block
|
||||
offset = (size_t)((uint8_t *)block_end - (uint8_t *)memory_pool);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -81,6 +88,10 @@ static void* find_free_space(size_t size) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void* find_free_space(size_t size) {
|
||||
return find_free_space_aligned(size, 8);
|
||||
}
|
||||
|
||||
// Calculate fragmentation
|
||||
static size_t calculate_fragmentation(void) {
|
||||
if (total_allocated == 0) return 0;
|
||||
|
|
@ -143,7 +154,7 @@ void memory_manager_init(void) {
|
|||
memory_manager_init_with_size(DEFAULT_POOL_SIZE);
|
||||
}
|
||||
|
||||
void* kmalloc(size_t size) {
|
||||
void* kmalloc_aligned(size_t size, size_t alignment) {
|
||||
if (!initialized) {
|
||||
memory_manager_init();
|
||||
}
|
||||
|
|
@ -162,8 +173,8 @@ void* kmalloc(size_t size) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// Find free space
|
||||
void *ptr = find_free_space(size);
|
||||
// Find free space with alignment
|
||||
void *ptr = find_free_space_aligned(size, alignment);
|
||||
if (ptr == NULL) {
|
||||
asm volatile("push %0; popfq" : : "r"(rflags));
|
||||
return NULL;
|
||||
|
|
@ -196,6 +207,10 @@ void* kmalloc(size_t size) {
|
|||
return ptr;
|
||||
}
|
||||
|
||||
void* kmalloc(size_t size) {
|
||||
return kmalloc_aligned(size, 8);
|
||||
}
|
||||
|
||||
void kfree(void *ptr) {
|
||||
if (ptr == NULL || !initialized) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ void memory_manager_init_with_size(size_t pool_size);
|
|||
|
||||
// Allocation/Deallocation
|
||||
void* kmalloc(size_t size);
|
||||
void* kmalloc_aligned(size_t size, size_t alignment);
|
||||
void kfree(void *ptr);
|
||||
void* krealloc(void *ptr, size_t new_size);
|
||||
|
||||
|
|
|
|||
121
src/kernel/paging.c
Normal file
121
src/kernel/paging.c
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#include "paging.h"
|
||||
#include "memory_manager.h"
|
||||
#include "platform.h"
|
||||
#include <stddef.h>
|
||||
|
||||
static uint64_t current_pml4_phys = 0;
|
||||
|
||||
// Get current CR3 value
|
||||
static uint64_t read_cr3(void) {
|
||||
uint64_t cr3;
|
||||
asm volatile("mov %%cr3, %0" : "=r"(cr3));
|
||||
return cr3;
|
||||
}
|
||||
|
||||
// Set CR3 value
|
||||
static void write_cr3(uint64_t cr3) {
|
||||
asm volatile("mov %0, %%cr3" : : "r"(cr3));
|
||||
}
|
||||
|
||||
// Helper to allocate a page table and clear it
|
||||
static uint64_t alloc_page_table_phys(void) {
|
||||
// We allocate a page table in Virtual Memory (HHDM)
|
||||
void* ptr = kmalloc(PAGE_SIZE * 2);
|
||||
if (!ptr) return 0;
|
||||
|
||||
// Align to PAGE_SIZE virtually
|
||||
uintptr_t addr = (uintptr_t)ptr;
|
||||
if (addr % PAGE_SIZE != 0) {
|
||||
addr = (addr + PAGE_SIZE) & ~(PAGE_SIZE - 1);
|
||||
}
|
||||
|
||||
page_table_t* table = (page_table_t*)addr;
|
||||
|
||||
// Clear table
|
||||
for (int i = 0; i < 512; i++) {
|
||||
table->entries[i] = 0;
|
||||
}
|
||||
|
||||
// Return the physical address of this table
|
||||
return v2p((uint64_t)table);
|
||||
}
|
||||
|
||||
void paging_init(void) {
|
||||
// Limine sets up a highly mapped page table for us.
|
||||
// CR3 contains the physical address of the PML4.
|
||||
current_pml4_phys = read_cr3() & PT_ADDR_MASK;
|
||||
}
|
||||
|
||||
uint64_t paging_get_pml4_phys(void) {
|
||||
return current_pml4_phys;
|
||||
}
|
||||
|
||||
void paging_switch_directory(uint64_t pml4_phys) {
|
||||
current_pml4_phys = pml4_phys;
|
||||
write_cr3(pml4_phys);
|
||||
}
|
||||
|
||||
void paging_map_page(uint64_t pml4_phys, uint64_t virtual_addr, uint64_t physical_addr, uint64_t flags) {
|
||||
if (!pml4_phys) return;
|
||||
|
||||
page_table_t* pml4 = (page_table_t*)p2v(pml4_phys);
|
||||
|
||||
// Extract indices
|
||||
uint64_t pml4_index = (virtual_addr >> 39) & 0x1FF;
|
||||
uint64_t pdpt_index = (virtual_addr >> 30) & 0x1FF;
|
||||
uint64_t pd_index = (virtual_addr >> 21) & 0x1FF;
|
||||
uint64_t pt_index = (virtual_addr >> 12) & 0x1FF;
|
||||
|
||||
// Check PML4 entry
|
||||
if (!(pml4->entries[pml4_index] & PT_PRESENT)) {
|
||||
uint64_t new_table_phys = alloc_page_table_phys();
|
||||
if (!new_table_phys) return; // Out of memory
|
||||
pml4->entries[pml4_index] = new_table_phys | PT_PRESENT | PT_RW | PT_USER;
|
||||
}
|
||||
|
||||
// Get PDPT
|
||||
page_table_t* pdpt = (page_table_t*)p2v(pml4->entries[pml4_index] & PT_ADDR_MASK);
|
||||
if (!(pdpt->entries[pdpt_index] & PT_PRESENT)) {
|
||||
uint64_t new_table_phys = alloc_page_table_phys();
|
||||
if (!new_table_phys) return;
|
||||
pdpt->entries[pdpt_index] = new_table_phys | PT_PRESENT | PT_RW | PT_USER;
|
||||
}
|
||||
|
||||
// Get PD
|
||||
page_table_t* pd = (page_table_t*)p2v(pdpt->entries[pdpt_index] & PT_ADDR_MASK);
|
||||
if (!(pd->entries[pd_index] & PT_PRESENT)) {
|
||||
uint64_t new_table_phys = alloc_page_table_phys();
|
||||
if (!new_table_phys) return;
|
||||
pd->entries[pd_index] = new_table_phys | PT_PRESENT | PT_RW | PT_USER;
|
||||
}
|
||||
|
||||
// Get PT
|
||||
page_table_t* pt = (page_table_t*)p2v(pd->entries[pd_index] & PT_ADDR_MASK);
|
||||
|
||||
// Set entry in PT
|
||||
pt->entries[pt_index] = (physical_addr & PT_ADDR_MASK) | flags;
|
||||
|
||||
// Flush TLB for this address
|
||||
asm volatile("invlpg (%0)" : : "r"(virtual_addr) : "memory");
|
||||
}
|
||||
|
||||
uint64_t paging_create_user_pml4_phys(void) {
|
||||
// 1. Allocate a new physical PML4
|
||||
uint64_t new_pml4_phys = alloc_page_table_phys();
|
||||
if (!new_pml4_phys) return 0;
|
||||
|
||||
page_table_t* new_pml4 = (page_table_t*)p2v(new_pml4_phys);
|
||||
|
||||
// 2. Clone the higher-half kernel mappings from the active PML4
|
||||
// In x86_64, indices 256-511 are the higher half.
|
||||
uint64_t kernel_pml4_phys = paging_get_pml4_phys();
|
||||
if (kernel_pml4_phys) {
|
||||
page_table_t* kernel_pml4 = (page_table_t*)p2v(kernel_pml4_phys);
|
||||
for (int i = 256; i < 512; i++) {
|
||||
new_pml4->entries[i] = kernel_pml4->entries[i];
|
||||
}
|
||||
}
|
||||
|
||||
// The lower half (0-255) is left empty for the user process to use
|
||||
return new_pml4_phys;
|
||||
}
|
||||
42
src/kernel/paging.h
Normal file
42
src/kernel/paging.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef PAGING_H
|
||||
#define PAGING_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
// Page Table Entry Flags
|
||||
#define PT_PRESENT (1ull << 0)
|
||||
#define PT_RW (1ull << 1)
|
||||
#define PT_USER (1ull << 2)
|
||||
#define PT_WRITE_THROUGH (1ull << 3)
|
||||
#define PT_CACHE_DISABLE (1ull << 4)
|
||||
#define PT_ACCESSED (1ull << 5)
|
||||
#define PT_DIRTY (1ull << 6)
|
||||
#define PT_HUGE (1ull << 7)
|
||||
#define PT_GLOBAL (1ull << 8)
|
||||
#define PT_NX (1ull << 63)
|
||||
|
||||
#define PT_ADDR_MASK 0x000FFFFFFFFFF000ull
|
||||
|
||||
typedef struct {
|
||||
uint64_t entries[512];
|
||||
} __attribute__((aligned(PAGE_SIZE))) page_table_t;
|
||||
|
||||
// Get the current PML4 physical address
|
||||
uint64_t paging_get_pml4_phys(void);
|
||||
|
||||
// Map a physical address to a virtual address
|
||||
void paging_map_page(uint64_t pml4_phys, uint64_t virtual_addr, uint64_t physical_addr, uint64_t flags);
|
||||
|
||||
// Create a new, isolated PML4 for a user process (returns physical address)
|
||||
uint64_t paging_create_user_pml4_phys(void);
|
||||
|
||||
// Switch the active page table (takes physical address)
|
||||
void paging_switch_directory(uint64_t pml4_phys);
|
||||
|
||||
// Initialize paging system (if needed beyond Limine's setup)
|
||||
void paging_init(void);
|
||||
|
||||
#endif // PAGING_H
|
||||
125
src/kernel/process.c
Normal file
125
src/kernel/process.c
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#include "process.h"
|
||||
#include "gdt.h"
|
||||
#include "idt.h"
|
||||
#include "paging.h"
|
||||
#include "io.h"
|
||||
#include "platform.h"
|
||||
#include "memory_manager.h"
|
||||
|
||||
extern void cmd_write(const char *str);
|
||||
extern void serial_write(const char *str);
|
||||
|
||||
#define MAX_PROCESSES 16
|
||||
static process_t processes[MAX_PROCESSES];
|
||||
static int process_count = 0;
|
||||
static process_t* current_process = NULL;
|
||||
static uint32_t next_pid = 0;
|
||||
|
||||
void process_init(void) {
|
||||
// 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;
|
||||
|
||||
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* stack = kmalloc_aligned(4096, 4096);
|
||||
void* kernel_stack = kmalloc_aligned(4096, 4096); // 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)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 + 4096);
|
||||
|
||||
*(--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
|
||||
|
||||
// Push 15 zeros for general purpose registers (r15 -> rax)
|
||||
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
|
||||
|
||||
new_proc->kernel_stack = (uint64_t)kernel_stack + 4096;
|
||||
new_proc->rsp = (uint64_t)stack_ptr;
|
||||
} else {
|
||||
// Kernel thread
|
||||
uint64_t* stack_ptr = (uint64_t*)((uint64_t)stack + 4096);
|
||||
*(--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
|
||||
|
||||
for (int i = 0; i < 15; i++) *(--stack_ptr) = 0;
|
||||
|
||||
new_proc->kernel_stack = 0;
|
||||
new_proc->rsp = (uint64_t)stack_ptr;
|
||||
}
|
||||
|
||||
// Add to linked list
|
||||
new_proc->next = current_process->next;
|
||||
current_process->next = new_proc;
|
||||
}
|
||||
|
||||
process_t* process_get_current(void) {
|
||||
return current_process;
|
||||
}
|
||||
|
||||
uint64_t process_schedule(uint64_t current_rsp) {
|
||||
if (!current_process || !current_process->next || current_process == current_process->next)
|
||||
return current_rsp;
|
||||
|
||||
// serial_write("SCHED\n");
|
||||
|
||||
// Save context
|
||||
current_process->rsp = current_rsp;
|
||||
|
||||
// Switch process
|
||||
current_process = current_process->next;
|
||||
|
||||
// Update Kernel Stack for User Mode interrupts
|
||||
if (current_process->is_user && current_process->kernel_stack) {
|
||||
tss_set_stack(current_process->kernel_stack);
|
||||
}
|
||||
|
||||
// Switch page table
|
||||
paging_switch_directory(current_process->pml4_phys);
|
||||
|
||||
return current_process->rsp;
|
||||
}
|
||||
|
||||
30
src/kernel/process.h
Normal file
30
src/kernel/process.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef PROCESS_H
|
||||
#define PROCESS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Registers saved on the stack by interrupts/exceptions
|
||||
typedef struct {
|
||||
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)) registers_t;
|
||||
|
||||
typedef struct process {
|
||||
uint32_t pid;
|
||||
uint64_t rsp; // Current stack pointer representing context
|
||||
uint64_t pml4_phys; // Physical address of the page table
|
||||
uint64_t kernel_stack; // Ring 0 stack pointer for user mode switches
|
||||
bool is_user;
|
||||
struct process *next;
|
||||
} process_t;
|
||||
|
||||
void process_init(void);
|
||||
void process_create(void* entry_point, bool is_user);
|
||||
process_t* process_get_current(void);
|
||||
uint64_t process_schedule(uint64_t current_rsp);
|
||||
|
||||
#endif
|
||||
|
||||
36
src/kernel/process_asm.asm
Normal file
36
src/kernel/process_asm.asm
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
global process_jump_usermode
|
||||
|
||||
section .text
|
||||
|
||||
; void process_jump_usermode(uint64_t entry_point, uint64_t user_stack)
|
||||
; System V AMD64 ABI:
|
||||
; RDI = entry_point
|
||||
; RSI = user_stack
|
||||
process_jump_usermode:
|
||||
cli
|
||||
|
||||
; Load user data segment (0x23)
|
||||
mov ax, 0x23
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
; Build the IRETQ stack frame
|
||||
; 1. SS (User Data Segment)
|
||||
push 0x23
|
||||
|
||||
; 2. RSP (User Stack)
|
||||
push rsi
|
||||
|
||||
; 3. RFLAGS (Enable Interrupts: IF = 0x200 | Reserved bit 1 = 0x2 -> 0x202)
|
||||
push 0x202
|
||||
|
||||
; 4. CS (User Code Segment)
|
||||
push 0x1B
|
||||
|
||||
; 5. RIP (Entry Point)
|
||||
push rdi
|
||||
|
||||
; Jump to Ring 3!
|
||||
iretq
|
||||
|
|
@ -8,10 +8,16 @@ extern void serial_print(const char *s);
|
|||
extern void serial_print_hex(uint64_t n);
|
||||
|
||||
// --- Timer Handler ---
|
||||
void timer_handler(void) {
|
||||
uint64_t timer_handler(uint64_t rsp) {
|
||||
wm_timer_tick();
|
||||
network_process_frames();
|
||||
|
||||
extern uint64_t process_schedule(uint64_t current_rsp);
|
||||
|
||||
outb(0x20, 0x20); // EOI to Master PIC
|
||||
rsp = process_schedule(rsp);
|
||||
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// --- Keyboard ---
|
||||
|
|
@ -35,13 +41,13 @@ static char scancode_map_shift[128] = {
|
|||
0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
void keyboard_handler(void) {
|
||||
uint64_t keyboard_handler(uint64_t rsp) {
|
||||
uint8_t scancode = inb(0x60);
|
||||
|
||||
if (scancode == 0xE0) {
|
||||
extended_scancode = true;
|
||||
outb(0x20, 0x20);
|
||||
return;
|
||||
return rsp;
|
||||
}
|
||||
|
||||
if (scancode == 0x2A || scancode == 0x36) { // Shift Down
|
||||
|
|
@ -71,6 +77,7 @@ void keyboard_handler(void) {
|
|||
}
|
||||
|
||||
outb(0x20, 0x20); // EOI
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// --- Mouse ---
|
||||
|
|
@ -128,12 +135,12 @@ void mouse_init(void) {
|
|||
mouse_read();
|
||||
}
|
||||
|
||||
void mouse_handler(void) {
|
||||
uint64_t mouse_handler(uint64_t rsp) {
|
||||
uint8_t status = inb(0x64);
|
||||
if (!(status & 0x20)) {
|
||||
outb(0x20, 0x20);
|
||||
outb(0xA0, 0x20);
|
||||
return;
|
||||
return rsp; // Return rsp here as well
|
||||
}
|
||||
|
||||
uint8_t b = inb(0x60);
|
||||
|
|
@ -162,6 +169,7 @@ void mouse_handler(void) {
|
|||
|
||||
outb(0x20, 0x20);
|
||||
outb(0xA0, 0x20); // Slave EOI
|
||||
return rsp;
|
||||
}
|
||||
|
||||
void ps2_init(void) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
#ifndef PS2_H
|
||||
#define PS2_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void ps2_init(void);
|
||||
void keyboard_handler(void);
|
||||
void mouse_handler(void);
|
||||
uint64_t timer_handler(uint64_t rsp);
|
||||
uint64_t keyboard_handler(uint64_t rsp);
|
||||
uint64_t mouse_handler(uint64_t rsp);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -33,9 +33,6 @@ void rtc_get_datetime(int *year, int *month, int *day, int *hour, int *minute, i
|
|||
*month = get_rtc_register(0x08);
|
||||
*year = get_rtc_register(0x09);
|
||||
|
||||
// Note: Century register is not standard, but often 0x32 if ACPI table says so.
|
||||
// For now, assume 20xx
|
||||
|
||||
do {
|
||||
last_second = *second;
|
||||
last_minute = *minute;
|
||||
|
|
|
|||
55
src/kernel/syscall.c
Normal file
55
src/kernel/syscall.c
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#include "syscall.h"
|
||||
#include "gdt.h"
|
||||
#include "memory_manager.h"
|
||||
|
||||
// Read MSR
|
||||
static inline uint64_t rdmsr(uint32_t msr) {
|
||||
uint32_t low, high;
|
||||
asm volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr));
|
||||
return ((uint64_t)high << 32) | low;
|
||||
}
|
||||
|
||||
// Write MSR
|
||||
static inline void wrmsr(uint32_t msr, uint64_t value) {
|
||||
uint32_t low = value & 0xFFFFFFFF;
|
||||
uint32_t high = value >> 32;
|
||||
asm volatile("wrmsr" : : "c"(msr), "a"(low), "d"(high));
|
||||
}
|
||||
|
||||
// Implemented in assembly
|
||||
extern void syscall_entry(void);
|
||||
|
||||
extern uint64_t kernel_syscall_stack;
|
||||
|
||||
void syscall_init(void) {
|
||||
void* stack = kmalloc(16384);
|
||||
kernel_syscall_stack = (uint64_t)stack + 16384;
|
||||
uint64_t efer = rdmsr(MSR_EFER);
|
||||
efer |= 1; // SCE bit is bit 0
|
||||
wrmsr(MSR_EFER, efer);
|
||||
|
||||
// STAR MSR setup:
|
||||
// Bits 32-47: Syscall CS and SS. CS = STAR[47:32], SS = STAR[47:32] + 8 (Kernel CS = 0x08)
|
||||
// Bits 48-63: Sysret CS and SS. CS = STAR[63:48] + 16, SS = STAR[63:48] + 8.
|
||||
// User Data must be Base+8, User Code must be Base+16.
|
||||
// Our GDT: User Data = 0x1B, User Code = 0x23.
|
||||
// Therefore Base = 0x13.
|
||||
uint64_t star = ((uint64_t)0x08 << 32) | ((uint64_t)0x13 << 48);
|
||||
wrmsr(MSR_STAR, star);
|
||||
|
||||
wrmsr(MSR_LSTAR, (uint64_t)syscall_entry);
|
||||
|
||||
// Mask Interrupts on SYSCALL (Clear IF bit in RFLAGS during syscall execution)
|
||||
wrmsr(MSR_FMASK, 0x200);
|
||||
}
|
||||
|
||||
void syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) {
|
||||
extern void cmd_write(const char *str);
|
||||
extern void serial_write(const char *str);
|
||||
|
||||
if (syscall_num == 1) { // SYS_WRITE
|
||||
// arg2 is the buffer based on our user_test logic
|
||||
cmd_write((const char*)arg2);
|
||||
serial_write((const char*)arg2);
|
||||
}
|
||||
}
|
||||
20
src/kernel/syscall.h
Normal file
20
src/kernel/syscall.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef SYSCALL_H
|
||||
#define SYSCALL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// 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_EXIT 60
|
||||
|
||||
void syscall_init(void);
|
||||
void syscall_handler_c(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5);
|
||||
|
||||
#endif // SYSCALL_H
|
||||
98
src/kernel/syscalls.asm
Normal file
98
src/kernel/syscalls.asm
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
global syscall_entry
|
||||
extern syscall_handler_c
|
||||
|
||||
section .text
|
||||
|
||||
; Syscall ABI:
|
||||
; RDI = syscall_num
|
||||
; RSI = arg1
|
||||
; RDX = arg2
|
||||
; R10 = arg3
|
||||
; R8 = arg4
|
||||
; R9 = arg5
|
||||
|
||||
syscall_entry:
|
||||
; We arrived here from Ring 3 via `syscall`.
|
||||
; RAX = syscall_num
|
||||
; RDI, RSI, RDX, R10, R8, R9 = args
|
||||
; RCX = User RIP
|
||||
; R11 = User RFLAGS
|
||||
; Current RSP = User RSP
|
||||
|
||||
; 1. Save User RSP
|
||||
mov [rel user_rsp_scratch], rsp
|
||||
|
||||
; 2. Switch to Kernel Stack
|
||||
mov rsp, [rel kernel_syscall_stack]
|
||||
|
||||
; 3. Save preserved registers (System V ABI)
|
||||
push rbx
|
||||
push rbp
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
; We also need to save RCX (RIP) and R11 (RFLAGS) because C functions might clobber them
|
||||
push rcx
|
||||
push r11
|
||||
|
||||
; Syscall convention: argument 4 is passed in R10, but C expects it in RCX
|
||||
mov rcx, r10
|
||||
|
||||
; The syscall number is in RAX, let's put it in RDI (arg 0 for C)
|
||||
; But wait, the ABI expects arg1 in RDI!
|
||||
; Let's change our C handler signature or adapt here.
|
||||
; C handler: void syscall_handler_c(uint64_t syscall_num, uint64_t arg1, ...)
|
||||
; So:
|
||||
; syscall_num -> RDI
|
||||
; arg1 (was RDI) -> RSI
|
||||
; arg2 (was RSI) -> RDX
|
||||
; arg3 (was RDX) -> RCX
|
||||
; arg4 (was R10) -> R8
|
||||
; arg5 (was R8) -> R9
|
||||
; arg6 (was R9) -> stack (if needed, but we have 6 regs)
|
||||
|
||||
; This shuffling is messy. Let's just push everything and call a struct-based handler,
|
||||
; or carefully shuffle.
|
||||
; For now, let's just make sure RAX goes to RDI, RDI to RSI, RSI to RDX, RDX to RCX, R10 to R8.
|
||||
|
||||
; Shuffling for SYS V C ABI:
|
||||
; R9 is arg6 -> no room in registers, need to push to stack (but our handler takes 6 args total)
|
||||
mov r9, r8 ; arg5
|
||||
mov r8, r10 ; arg4
|
||||
mov rcx, rdx ; arg3
|
||||
mov rdx, rsi ; arg2
|
||||
mov rsi, rdi ; arg1
|
||||
mov rdi, rax ; syscall_num
|
||||
|
||||
; 4. Call C handler
|
||||
call syscall_handler_c
|
||||
|
||||
; 5. Restore RCX and R11
|
||||
pop r11
|
||||
pop rcx
|
||||
|
||||
; 6. Restore preserved registers
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbp
|
||||
pop rbx
|
||||
|
||||
; 7. Restore User RSP
|
||||
mov rsp, [rel user_rsp_scratch]
|
||||
|
||||
; 8. Return to User Mode
|
||||
; NASM syntax for 64-bit sysret requires the o64 prefix
|
||||
; Force IF=1 (bit 9) in R11 (restored to RFLAGS) to ensure interrupts stay enabled!
|
||||
or r11, 0x200
|
||||
|
||||
o64 sysret
|
||||
|
||||
section .bss
|
||||
global kernel_syscall_stack
|
||||
global user_rsp_scratch
|
||||
kernel_syscall_stack: resq 1
|
||||
user_rsp_scratch: resq 1
|
||||
16
src/kernel/test_syscall.asm
Normal file
16
src/kernel/test_syscall.asm
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
global test_syscall
|
||||
section .text
|
||||
|
||||
test_syscall:
|
||||
; syscall number in RDI
|
||||
mov rdi, 1
|
||||
; string pointer in RSI
|
||||
lea rsi, [rel test_msg]
|
||||
|
||||
; The SYSCALL instruction
|
||||
syscall
|
||||
|
||||
ret
|
||||
|
||||
section .rodata
|
||||
test_msg: db "Hello from Syscall!", 10, 0
|
||||
22
src/kernel/user_test.asm
Normal file
22
src/kernel/user_test.asm
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
global user_test_function
|
||||
|
||||
section .text
|
||||
user_test_function:
|
||||
; Syscall convention
|
||||
.loop:
|
||||
; Invoke SYS_WRITE (Syscall #1)
|
||||
mov rdi, 1 ; arg1: fd = 1 (stdout)
|
||||
lea rsi, [rel msg] ; arg2: buffer (RIP-relative)
|
||||
mov rdx, 15 ; arg3: length
|
||||
mov eax, 1 ; syscall_num = 1 (SYS_WRITE)
|
||||
syscall
|
||||
|
||||
; Some delay loop
|
||||
mov rcx, 100000000
|
||||
.delay:
|
||||
dec rcx
|
||||
jnz .delay
|
||||
|
||||
jmp .loop
|
||||
|
||||
msg: db "Hello syscall!", 10
|
||||
Loading…
Reference in a new issue