mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 02:16:58 +00:00
Ring 3 multitasking
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user