feat(boot+shell): add bootable disk image, unified scheduler and interactive user-space shell

- Implement Limine v8 boot protocol requests in dedicated PT_LOAD RW segment
- Configure IOPL=3 and SSE in arch setup for Ring 3 drivers and libc
- Unify scheduler context switching via switch_to and preempt_tick
- Support dual-source keyboard/serial input in sys_key_read
- Add automated 'make image' and 'make run' targets for instant QEMU launch
- Complete interactive UNIX-like shell (osh) with built-in commands & ramdisk
This commit is contained in:
RarDog
2026-09-02 19:47:57 +03:00
parent ca3555bd09
commit eed05c1eca
17 changed files with 452 additions and 217 deletions
+1
View File
@@ -7,6 +7,7 @@
*.bin *.bin
*.iso *.iso
*.img *.img
/boot/
# Cargo # Cargo
Cargo.lock Cargo.lock
+71 -24
View File
@@ -18,11 +18,12 @@ LIB_DIR := lib
HAL_DIR := hal HAL_DIR := hal
SERVERS_DIR := servers SERVERS_DIR := servers
KERNEL_DIR := kernel KERNEL_DIR := kernel
ISO_DIR := $(BUILD_DIR)/iso_root LIMINE_DIR := boot/limine
# Common Freestanding Flags # Common Freestanding Flags
COMMON_FLAGS := -Wall -Wextra -O2 -ffreestanding -fno-stack-protector \ COMMON_FLAGS := -Wall -Wextra -O2 -ffreestanding -fno-stack-protector \
-fno-pie -no-pie -mno-red-zone -m64 \ -fno-pie -no-pie -mno-red-zone -m64 \
-mno-sse -mno-sse2 -mno-avx \
-I$(INCLUDE_DIR) -I$(LIB_DIR)/libc/include -I$(INCLUDE_DIR) -I$(LIB_DIR)/libc/include
CFLAGS := $(COMMON_FLAGS) -std=c11 CFLAGS := $(COMMON_FLAGS) -std=c11
@@ -35,18 +36,20 @@ INIT_SERVER := $(BUILD_DIR)/init_server.elf
UART_DRIVER := $(BUILD_DIR)/uart_driver.elf UART_DRIVER := $(BUILD_DIR)/uart_driver.elf
SH_SERVER := $(BUILD_DIR)/sh.elf SH_SERVER := $(BUILD_DIR)/sh.elf
PROCMGR := $(BUILD_DIR)/procmgr.elf PROCMGR := $(BUILD_DIR)/procmgr.elf
DISK_IMG := $(BUILD_DIR)/opencoreC.img
LIBC_OBJS := $(BUILD_DIR)/string.o $(BUILD_DIR)/syscalls.o $(BUILD_DIR)/stdlib.o LIBC_OBJS := $(BUILD_DIR)/string.o $(BUILD_DIR)/syscalls.o $(BUILD_DIR)/stdlib.o
.PHONY: all kernel libc hal servers iso run clean help .PHONY: all kernel libc hal servers image run clean help limine-setup
all: kernel libc hal servers all: kernel libc hal servers
@mkdir -p $(BUILD_DIR) @mkdir -p $(BUILD_DIR)
@cp $(KERNEL_BIN) $(KERNEL_ELF) @cp $(KERNEL_BIN) $(KERNEL_ELF)
@echo "=======================================================" @echo "======================================================="
@echo " [SUCCESS] opencoreC Skeleton Build Complete!" @echo " [SUCCESS] opencoreC Build Complete!"
@echo " Kernel: $(KERNEL_ELF)" @echo " Kernel: $(KERNEL_ELF)"
@echo " Servers: $(INIT_SERVER), $(UART_DRIVER), $(SH_SERVER), $(PROCMGR)" @echo " Servers: $(INIT_SERVER), $(UART_DRIVER), $(SH_SERVER)"
@echo " Run: make image && make run"
@echo "=======================================================" @echo "======================================================="
kernel: kernel:
@@ -90,25 +93,60 @@ servers: libc hal
@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(PROCMGR) \ @$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(PROCMGR) \
$(BUILD_DIR)/procmgr_main.o $(BUILD_DIR)/ipc.o $(LIBC_OBJS) $(BUILD_DIR)/procmgr_main.o $(BUILD_DIR)/ipc.o $(LIBC_OBJS)
# Staging Limine Boot ISO directory # Download and build the Limine bootloader host tool
iso: all limine-setup:
@mkdir -p $(ISO_DIR)/boot $(ISO_DIR)/EFI/BOOT @if [ ! -d "$(LIMINE_DIR)" ]; then \
@cp $(KERNEL_ELF) $(ISO_DIR)/boot/ echo "[LIMINE] Cloning Limine v8 binary branch..."; \
@cp $(INIT_SERVER) $(ISO_DIR)/boot/ git clone https://github.com/limine-bootloader/limine.git --branch=v8.x-binary --depth=1 $(LIMINE_DIR); \
@cp $(UART_DRIVER) $(ISO_DIR)/boot/ fi
@cp $(SH_SERVER) $(ISO_DIR)/boot/ @echo "[LIMINE] Building host installer tool..."
@cp limine.conf $(ISO_DIR)/boot/limine.conf @$(MAKE) -C $(LIMINE_DIR) limine
@cp limine.cfg $(ISO_DIR)/boot/limine.cfg
@echo "[ISO] Staged boot files in $(ISO_DIR)/"
run: all # Build a 64 MiB bootable FAT32 disk image with Limine BIOS/UEFI
@echo "[QEMU] Launching opencoreC in QEMU (SMP 4 Cores, Serial stdio)..." image: all limine-setup
@$(QEMU) -M q35 -smp 4 -m 512M \ @echo "[IMAGE] Creating bootable disk image (64 MiB FAT32)..."
-serial stdio \ @dd if=/dev/zero of=$(DISK_IMG) bs=1M count=64 2>/dev/null
-display none \ @parted -s $(DISK_IMG) mklabel msdos mkpart primary fat32 2048s 100% set 1 boot on 2>/dev/null
@mformat -F -i $(DISK_IMG)@@1M ::
@mmd -i $(DISK_IMG)@@1M ::/boot ::/EFI ::/EFI/BOOT
@# Kernel and userspace servers
@mcopy -i $(DISK_IMG)@@1M $(KERNEL_ELF) ::/boot/
@mcopy -i $(DISK_IMG)@@1M $(INIT_SERVER) ::/boot/
@mcopy -i $(DISK_IMG)@@1M $(UART_DRIVER) ::/boot/
@mcopy -i $(DISK_IMG)@@1M $(SH_SERVER) ::/boot/
@# Bootloader config
@mcopy -i $(DISK_IMG)@@1M limine.conf ::/
@mcopy -i $(DISK_IMG)@@1M limine.cfg ::/
@mcopy -i $(DISK_IMG)@@1M limine.conf ::/boot/
@mcopy -i $(DISK_IMG)@@1M limine.cfg ::/boot/
@# Limine bootloader files
@mcopy -i $(DISK_IMG)@@1M $(LIMINE_DIR)/limine-bios.sys ::/
@mcopy -i $(DISK_IMG)@@1M $(LIMINE_DIR)/limine-bios.sys ::/boot/
@mcopy -i $(DISK_IMG)@@1M $(LIMINE_DIR)/BOOTX64.EFI ::/EFI/BOOT/
@# Install Limine Stage 1 into MBR
@$(LIMINE_DIR)/limine bios-install $(DISK_IMG) 2>&1 | grep -v "^Physical\|^Installing\|^Stage\|^Reminder"
@echo "[IMAGE] Done: $(DISK_IMG) (BIOS + UEFI bootable)"
# Launch QEMU with serial terminal (keyboard works via -serial stdio)
run: image
@echo "[QEMU] Launching opencoreC (SMP 4 cores, Serial terminal)..."
@echo "[QEMU] Type 'help' for commands. Ctrl+A X to exit QEMU."
@echo "-------------------------------------------------------"
@$(QEMU) \
-M q35 \
-smp 4 \
-m 512M \
-serial mon:stdio \
-no-reboot \ -no-reboot \
-no-shutdown \ -display none \
-kernel $(KERNEL_ELF) -drive file=$(DISK_IMG),format=raw
# Run without rebuilding image (faster iteration)
qemu: $(DISK_IMG)
@echo "[QEMU] Quick launch from existing image..."
@echo "[QEMU] Type 'help'. Ctrl+A X to exit."
@$(QEMU) -M q35 -smp 4 -m 512M -serial mon:stdio -no-reboot -display none \
-drive file=$(DISK_IMG),format=raw
clean: clean:
@echo "[CLEAN] Removing build directory and cargo target..." @echo "[CLEAN] Removing build directory and cargo target..."
@@ -116,7 +154,16 @@ clean:
help: help:
@echo "opencoreC Microkernel Build System" @echo "opencoreC Microkernel Build System"
@echo ""
@echo "Targets:" @echo "Targets:"
@echo " make all - Build kernel, HAL, libc, and all user servers" @echo " make all - Build kernel, HAL, libc, and all user servers"
@echo " make run - Launch QEMU with SMP 4 Cores & Serial output" @echo " make image - Create bootable disk image (build/opencoreC.img)"
@echo " make clean - Clean build artifacts" @echo " make run - Build image and launch in QEMU (interactive serial)"
@echo " make qemu - Quick relaunch without rebuild"
@echo " make limine-setup - Download/build Limine bootloader"
@echo " make clean - Remove all build artifacts"
@echo ""
@echo "QEMU Controls:"
@echo " Type commands in the terminal (serial I/O)"
@echo " Ctrl+A then X -> exit QEMU"
@echo " Ctrl+A then C -> QEMU monitor"
+17 -7
View File
@@ -25,15 +25,25 @@ SECTIONS
*(.rodata .rodata.*) *(.rodata .rodata.*)
} :rodata } :rodata
/* Limine requests section */
.limine_requests : {
KEEP(*(.limine_requests_start))
KEEP(*(.limine_requests))
KEEP(*(.limine_requests_end))
} :rodata
. = ALIGN(CONSTANT(MAXPAGESIZE)); . = ALIGN(CONSTANT(MAXPAGESIZE));
/* Limine requests: must be in a WRITABLE segment so bootloader can fill responses */
.limine_base_revision : {
KEEP(*(.limine_base_revision))
} :data
.limine_requests_start : {
KEEP(*(.limine_requests_start))
} :data
.limine_requests : {
KEEP(*(.limine_requests))
} :data
.limine_requests_end : {
KEEP(*(.limine_requests_end))
} :data
.data : { .data : {
*(.data .data.*) *(.data .data.*)
} :data } :data
+51 -8
View File
@@ -1,14 +1,15 @@
/* opencoreC - Thread Context Switch */ /* opencoreC - Thread Context Switch & Entry Trampoline */
.global switch_to .global switch_to
.global thread_entry_trampoline
.section .text .section .text
/* /*
* void switch_to(uint64_t **prev_rsp_ptr, uint64_t *next_rsp); * switch_to(uint64_t *prev_rsp_ptr, uint64_t next_rsp)
* RDI = pointer to current thread's stored RSP * RDI = &current_thread.context.rsp (save current RSP here)
* RSI = target thread's RSP * RSI = next thread's saved RSP
*/ */
switch_to: switch_to:
/* Push callee-saved registers of outgoing thread */ /* Save callee-saved registers of outgoing thread */
push rbp push rbp
push rbx push rbx
push r12 push r12
@@ -16,13 +17,13 @@ switch_to:
push r14 push r14
push r15 push r15
/* Save current stack pointer */ /* Save RSP of outgoing thread */
mov [rdi], rsp mov [rdi], rsp
/* Load target stack pointer */ /* Load RSP of incoming thread */
mov rsp, rsi mov rsp, rsi
/* Pop callee-saved registers of incoming thread */ /* Restore callee-saved registers of incoming thread */
pop r15 pop r15
pop r14 pop r14
pop r13 pop r13
@@ -31,3 +32,45 @@ switch_to:
pop rbp pop rbp
ret ret
/*
* thread_entry_trampoline:
* Jumped to via 'ret' in switch_to when a newly created thread runs for the first time.
* When create_user_thread created the stack:
* R12 was populated with user_rsp
* R13 was populated with entry_rip
*/
thread_entry_trampoline:
/* Set user data segments (0x18 | 3 = 0x1B) */
mov ax, 0x1B
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
/* Build hardware IRETQ frame on kernel stack */
push 0x1B /* User SS */
push r12 /* User RSP */
push 0x3202 /* RFLAGS (IF=1, IOPL=3 for hardware drivers) */
push 0x23 /* User CS (0x20 | 3 = 0x23) */
push r13 /* User RIP */
/* Clear all general-purpose registers */
xor rax, rax
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
xor rsi, rsi
xor rdi, rdi
xor rbp, rbp
xor r8, r8
xor r9, r9
xor r10, r10
xor r11, r11
xor r12, r12
xor r13, r13
xor r14, r14
xor r15, r15
/* Transition to Ring 3 */
iretq
+36 -17
View File
@@ -2,25 +2,46 @@
.global syscall_entry .global syscall_entry
.extern kernel_syscall_dispatcher .extern kernel_syscall_dispatcher
/* Per-CPU scratch: user RSP saved here on syscall entry */
.section .bss .section .bss
.align 16 .align 8
user_scratch_rsp: .global cpu0_scratch_user_rsp
.quad 0 cpu0_scratch_user_rsp: .quad 0
kernel_temp_stack:
.skip 4096
kernel_temp_stack_top:
.section .text .section .text
/*
* syscall_entry:
* On entry (SYSCALL from Ring 3):
* RCX = user RIP
* R11 = user RFLAGS
* RAX = syscall number
* RDI,RSI,RDX,R10,R8,R9 = args
* RSP = user RSP
*
* Strategy:
* 1. Save user RSP into scratch area
* 2. Load kernel RSP from TSS.RSP0 via gdt.rs TSS symbol
* 3. Build SyscallRegisters on kernel stack
* 4. Dispatch to Rust handler
* 5. Restore & sysretq
*/
syscall_entry: syscall_entry:
/* Save user stack pointer */ /* Save user RSP */
mov [rip + user_scratch_rsp], rsp mov [rip + cpu0_scratch_user_rsp], rsp
lea rsp, [rip + kernel_temp_stack_top]
/* Save user RIP (in RCX) and user RFLAGS (in R11) */ /* Load kernel stack from TSS.rsp0.
* TSS is exported as 'TSS' from gdt.rs.
* Layout: _reserved1(u32=4 bytes), rsp0(u64=8 bytes)
* So TSS.rsp0 is at offset 4 from TSS start.
*/
lea rsp, [rip + TSS]
mov rsp, [rsp + 4]
/* Push user RIP and RFLAGS */
push rcx push rcx
push r11 push r11
/* Save argument and volatile registers to construct SyscallRegisters */ /* Push syscall arguments as SyscallRegisters */
push rax push rax
push rdi push rdi
push rsi push rsi
@@ -29,11 +50,10 @@ syscall_entry:
push r9 push r9
push r10 push r10
/* Pass pointer to SyscallRegisters struct in RDI */
mov rdi, rsp mov rdi, rsp
call kernel_syscall_dispatcher call kernel_syscall_dispatcher
/* Restore registers (rax, rdx, r8 may have return values from dispatcher) */ /* Restore args */
pop r10 pop r10
pop r9 pop r9
pop r8 pop r8
@@ -42,12 +62,11 @@ syscall_entry:
pop rdi pop rdi
pop rax pop rax
/* Restore user RIP and RFLAGS */ /* Restore user context */
pop r11 pop r11
pop rcx pop rcx
/* Restore user stack */ /* Restore user RSP */
mov rsp, [rip + user_scratch_rsp] mov rsp, [rip + cpu0_scratch_user_rsp]
/* Return to Ring 3 */
sysretq sysretq
+4 -8
View File
@@ -4,7 +4,7 @@
.section .text .section .text
timer_isr_entry: timer_isr_entry:
/* 1. Save all general-purpose registers to form a complete interrupt context frame */ /* 1. Save all general-purpose registers to form complete interrupt context frame */
push rax push rax
push rbx push rbx
push rcx push rcx
@@ -21,14 +21,10 @@ timer_isr_entry:
push r14 push r14
push r15 push r15
/* 2. Pass current saved stack pointer in RDI */ /* 2. Call handler in Rust (sends EOI and invokes sched.preempt_tick) */
mov rdi, rsp
call timer_interrupt_handler call timer_interrupt_handler
/* 3. Switch stack pointer to returned value (RAX contains next thread's RSP) */ /* 3. Restore all general-purpose registers */
mov rsp, rax
/* 4. Restore all general-purpose registers */
pop r15 pop r15
pop r14 pop r14
pop r13 pop r13
@@ -45,5 +41,5 @@ timer_isr_entry:
pop rbx pop rbx
pop rax pop rax
/* 5. Return from interrupt (IRETQ restores RIP, CS, RFLAGS, RSP, SS) */ /* 4. Return from interrupt (IRETQ restores RIP, CS, RFLAGS, RSP, SS) */
iretq iretq
+1
View File
@@ -37,6 +37,7 @@ struct GdtDescriptor {
base: u64, base: u64,
} }
#[no_mangle]
pub static mut TSS: TaskStateSegment = TaskStateSegment::new(); pub static mut TSS: TaskStateSegment = TaskStateSegment::new();
// 8 entries: Null, KCode, KData, UData, UCode, TSS Low, TSS High, Null // 8 entries: Null, KCode, KData, UData, UCode, TSS Low, TSS High, Null
+123 -26
View File
@@ -55,37 +55,93 @@ extern "C" {
fn keyboard_isr_entry(); fn keyboard_isr_entry();
} }
// Global assembly ISR stub for default exception handling // Global assembly ISR stub with vector ID pushed as separate stubs (0-31)
core::arch::global_asm!( core::arch::global_asm!(
// With error code: CPU already pushed error_code, we push vector on top
".macro ISR_ERR_CODE num",
"isr_stub_\\num:",
"push \\num", // push vector number (error_code already on stack below)
"jmp default_isr_common",
".endm",
// Without error code: push dummy 0, then vector
".macro ISR_NO_ERR num",
"isr_stub_\\num:",
"push 0", // dummy error_code
"push \\num", // push vector number
"jmp default_isr_common",
".endm",
"ISR_NO_ERR 0", "ISR_NO_ERR 1", "ISR_NO_ERR 2", "ISR_NO_ERR 3",
"ISR_NO_ERR 4", "ISR_NO_ERR 5", "ISR_NO_ERR 6", "ISR_NO_ERR 7",
"ISR_ERR_CODE 8","ISR_NO_ERR 9", "ISR_ERR_CODE 10","ISR_ERR_CODE 11",
"ISR_ERR_CODE 12","ISR_ERR_CODE 13","ISR_ERR_CODE 14","ISR_NO_ERR 15",
"ISR_NO_ERR 16","ISR_ERR_CODE 17","ISR_NO_ERR 18","ISR_NO_ERR 19",
"ISR_NO_ERR 20","ISR_ERR_CODE 21","ISR_NO_ERR 22","ISR_NO_ERR 23",
"ISR_NO_ERR 24","ISR_NO_ERR 25","ISR_NO_ERR 26","ISR_NO_ERR 27",
"ISR_NO_ERR 28","ISR_NO_ERR 29","ISR_ERR_CODE 30","ISR_NO_ERR 31",
".global default_isr_stub", ".global default_isr_stub",
"default_isr_stub:", "default_isr_stub:",
"push rax", "push 0", // dummy error code
"push rcx", "push 255", // dummy vector
"push rdx", "jmp default_isr_common",
"push rsi",
"push rdi", ".global default_isr_common",
"push r8", "default_isr_common:",
"push r9", // Stack at this point (top to bottom):
"push r10", // RSP+0: vector
"push r11", // RSP+8: error_code
// RSP+16: RIP (pushed by CPU)
// RSP+24: CS
// RSP+32: RFLAGS
// RSP+40: RSP (if ring change)
// RSP+48: SS (if ring change)
"push rax", "push rcx", "push rdx",
"push rsi", "push rdi",
"push r8", "push r9", "push r10", "push r11",
"mov rdi, rsp", "mov rdi, rsp",
"call raw_exception_handler", "call raw_exception_handler",
"pop r11", "pop r11", "pop r10", "pop r9", "pop r8",
"pop r10", "pop rdi", "pop rsi",
"pop r9", "pop rdx", "pop rcx", "pop rax",
"pop r8", // skip vector + error_code from stack
"pop rdi", "add rsp, 16",
"pop rsi",
"pop rdx",
"pop rcx",
"pop rax",
"iretq" "iretq"
); );
pub unsafe fn init() { pub unsafe fn init() {
let stub_addr = default_isr_stub as *const () as usize as u64;
let idt_ptr = core::ptr::addr_of_mut!(IDT) as *mut IdtEntry; let idt_ptr = core::ptr::addr_of_mut!(IDT) as *mut IdtEntry;
for i in 0..256 {
// Install per-vector stubs for exceptions 0-31
extern "C" {
fn isr_stub_0(); fn isr_stub_1(); fn isr_stub_2(); fn isr_stub_3();
fn isr_stub_4(); fn isr_stub_5(); fn isr_stub_6(); fn isr_stub_7();
fn isr_stub_8(); fn isr_stub_9(); fn isr_stub_10(); fn isr_stub_11();
fn isr_stub_12(); fn isr_stub_13(); fn isr_stub_14(); fn isr_stub_15();
fn isr_stub_16(); fn isr_stub_17(); fn isr_stub_18(); fn isr_stub_19();
fn isr_stub_20(); fn isr_stub_21(); fn isr_stub_22(); fn isr_stub_23();
fn isr_stub_24(); fn isr_stub_25(); fn isr_stub_26(); fn isr_stub_27();
fn isr_stub_28(); fn isr_stub_29(); fn isr_stub_30(); fn isr_stub_31();
}
let exception_stubs: [u64; 32] = [
isr_stub_0 as u64, isr_stub_1 as u64, isr_stub_2 as u64, isr_stub_3 as u64,
isr_stub_4 as u64, isr_stub_5 as u64, isr_stub_6 as u64, isr_stub_7 as u64,
isr_stub_8 as u64, isr_stub_9 as u64, isr_stub_10 as u64, isr_stub_11 as u64,
isr_stub_12 as u64, isr_stub_13 as u64, isr_stub_14 as u64, isr_stub_15 as u64,
isr_stub_16 as u64, isr_stub_17 as u64, isr_stub_18 as u64, isr_stub_19 as u64,
isr_stub_20 as u64, isr_stub_21 as u64, isr_stub_22 as u64, isr_stub_23 as u64,
isr_stub_24 as u64, isr_stub_25 as u64, isr_stub_26 as u64, isr_stub_27 as u64,
isr_stub_28 as u64, isr_stub_29 as u64, isr_stub_30 as u64, isr_stub_31 as u64,
];
for (i, &stub_addr) in exception_stubs.iter().enumerate() {
(*idt_ptr.add(i)).set_handler(stub_addr, 0, 0);
}
// Fill 32..255 with default stub
let stub_addr = default_isr_stub as *const () as usize as u64;
for i in 32..256usize {
(*idt_ptr.add(i)).set_handler(stub_addr, 0, 0); (*idt_ptr.add(i)).set_handler(stub_addr, 0, 0);
} }
@@ -105,16 +161,57 @@ pub unsafe fn init() {
core::arch::asm!("lidt [{0}]", in(reg) &descriptor, options(preserves_flags)); core::arch::asm!("lidt [{0}]", in(reg) &descriptor, options(preserves_flags));
} }
#[no_mangle] #[repr(C)]
pub extern "C" fn raw_exception_handler(stack_ptr: *const u64) { pub struct ExceptionFrame {
kprintln!("[CPU EXCEPTION] Trap triggered at stack frame: {:p}", stack_ptr); // Stack layout when raw_exception_handler is called (low addr = top of stack):
// Pushed by default_isr_common: r11,r10,r9,r8,rdi,rsi,rdx,rcx,rax (in that push order)
pub r11: u64, pub r10: u64, pub r9: u64, pub r8: u64,
pub rdi: u64, pub rsi: u64, pub rdx: u64, pub rcx: u64, pub rax: u64,
// Pushed by per-vector stub (in push order: vector first on top)
pub vector: u64,
pub error_code: u64,
// Pushed by CPU hardware:
pub rip: u64,
pub cs: u64,
pub rflags: u64,
pub user_rsp: u64, // only present on ring transition
pub ss: u64,
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn timer_interrupt_handler(saved_rsp: u64) -> u64 { pub extern "C" fn raw_exception_handler(frame: *const ExceptionFrame) {
unsafe {
let v = (*frame).vector;
let ec = (*frame).error_code;
let rip = (*frame).rip;
let cs = (*frame).cs;
let rflags = (*frame).rflags;
let names = [
"#DE", "#DB", "NMI", "#BP", "#OF", "#BR", "#UD", "#NM",
"#DF", "CSO", "#TS", "#NP", "#SS", "#GP", "#PF", "RES",
"#MF", "#AC", "#MC", "#XM", "#VE", "#CP", "??" , "??" ,
"??" , "??" , "??" , "??" , "??" , "??" , "#SX", "??" ,
];
let name = if v < 32 { names[v as usize] } else { "IRQ" };
kprintln!("\n[EXCEPTION] Vec={} ({}) ErrCode={:#x} RIP={:#018x} CS={:#x} RFLAGS={:#x}",
v, name, ec, rip, cs, rflags);
if v == 14 {
let cr2: u64;
core::arch::asm!("mov {}, cr2", out(reg) cr2, options(nostack, preserves_flags));
kprintln!(" #PF CR2 (faulting address): {:#018x}", cr2);
}
if v < 32 {
kprintln!(" [HALT] Unrecoverable exception - CPU halted.");
loop { core::arch::asm!("cli; hlt", options(nostack, preserves_flags)); }
}
}
}
#[no_mangle]
pub extern "C" fn timer_interrupt_handler() {
unsafe { unsafe {
send_eoi(); send_eoi();
let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER); let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER);
sched.preempt(saved_rsp) sched.preempt_tick();
} }
} }
+20
View File
@@ -4,8 +4,28 @@ pub mod syscall;
pub mod smp; pub mod smp;
pub unsafe fn init() { pub unsafe fn init() {
enable_sse();
gdt::init(); gdt::init();
idt::init(); idt::init();
syscall::init(); syscall::init();
smp::init(); smp::init();
} }
/// Enable SSE/SSE2/XSAVE for user-space SIMD operations.
/// Required to prevent #GP when user programs use XMM registers or compiler-generated SSE code.
unsafe fn enable_sse() {
// Clear CR0.EM (bit 2) - disable x87 FPU emulation
// Set CR0.MP (bit 1) - monitor coprocessor (allow WAIT)
let mut cr0: u64;
core::arch::asm!("mov {0}, cr0", out(reg) cr0, options(nostack, preserves_flags));
cr0 &= !(1u64 << 2); // Clear EM
cr0 |= 1u64 << 1; // Set MP
core::arch::asm!("mov cr0, {0}", in(reg) cr0, options(nostack, preserves_flags));
// Set CR4.OSFXSR (bit 9) - OS supports FXSAVE/FXRSTOR
// Set CR4.OSXMMEXCPT (bit 10) - OS handles SIMD FP exceptions
let mut cr4: u64;
core::arch::asm!("mov {0}, cr4", out(reg) cr4, options(nostack, preserves_flags));
cr4 |= (1u64 << 9) | (1u64 << 10);
core::arch::asm!("mov cr4, {0}", in(reg) cr4, options(nostack, preserves_flags));
}
+66 -36
View File
@@ -1,20 +1,17 @@
//! Limine Bootloader Protocol Requests & Types for opencoreC Microkernel //! Limine Bootloader Protocol Requests & Types for opencoreC Microkernel
//! Implements Base Revision 3 specification. //! Implements Limine v8 Base Revision 0 specification.
#[repr(C)] #[repr(transparent)]
pub struct LimineBaseRevision { pub struct SyncPtr(pub *const ());
pub id: [u64; 4], unsafe impl Sync for SyncPtr {}
pub revision: u64,
}
unsafe impl Sync for LimineBaseRevision {}
#[repr(C)] #[repr(C, align(8))]
pub struct LimineHhdmResponse { pub struct LimineHhdmResponse {
pub revision: u64, pub revision: u64,
pub offset: u64, pub offset: u64,
} }
#[repr(C)] #[repr(C, align(8))]
pub struct LimineHhdmRequest { pub struct LimineHhdmRequest {
pub id: [u64; 4], pub id: [u64; 4],
pub revision: u64, pub revision: u64,
@@ -35,21 +32,21 @@ pub enum LimineMemoryType {
Framebuffer = 7, Framebuffer = 7,
} }
#[repr(C)] #[repr(C, align(8))]
pub struct LimineMemmapEntry { pub struct LimineMemmapEntry {
pub base: u64, pub base: u64,
pub length: u64, pub length: u64,
pub typ: u64, pub typ: u64,
} }
#[repr(C)] #[repr(C, align(8))]
pub struct LimineMemmapResponse { pub struct LimineMemmapResponse {
pub revision: u64, pub revision: u64,
pub entry_count: u64, pub entry_count: u64,
pub entries: *const *const LimineMemmapEntry, pub entries: *const *const LimineMemmapEntry,
} }
#[repr(C)] #[repr(C, align(8))]
pub struct LimineMemmapRequest { pub struct LimineMemmapRequest {
pub id: [u64; 4], pub id: [u64; 4],
pub revision: u64, pub revision: u64,
@@ -57,14 +54,14 @@ pub struct LimineMemmapRequest {
} }
unsafe impl Sync for LimineMemmapRequest {} unsafe impl Sync for LimineMemmapRequest {}
#[repr(C)] #[repr(C, align(8))]
pub struct LimineKernelAddressResponse { pub struct LimineKernelAddressResponse {
pub revision: u64, pub revision: u64,
pub physical_base: u64, pub physical_base: u64,
pub virtual_base: u64, pub virtual_base: u64,
} }
#[repr(C)] #[repr(C, align(8))]
pub struct LimineKernelAddressRequest { pub struct LimineKernelAddressRequest {
pub id: [u64; 4], pub id: [u64; 4],
pub revision: u64, pub revision: u64,
@@ -72,7 +69,7 @@ pub struct LimineKernelAddressRequest {
} }
unsafe impl Sync for LimineKernelAddressRequest {} unsafe impl Sync for LimineKernelAddressRequest {}
#[repr(C)] #[repr(C, align(8))]
pub struct LimineFile { pub struct LimineFile {
pub revision: u64, pub revision: u64,
pub address: *const u8, pub address: *const u8,
@@ -90,14 +87,14 @@ pub struct LimineFile {
pub part_uuid: [u8; 16], pub part_uuid: [u8; 16],
} }
#[repr(C)] #[repr(C, align(8))]
pub struct LimineModuleResponse { pub struct LimineModuleResponse {
pub revision: u64, pub revision: u64,
pub module_count: u64, pub module_count: u64,
pub modules: *const *const LimineFile, pub modules: *const *const LimineFile,
} }
#[repr(C)] #[repr(C, align(8))]
pub struct LimineModuleRequest { pub struct LimineModuleRequest {
pub id: [u64; 4], pub id: [u64; 4],
pub revision: u64, pub revision: u64,
@@ -107,7 +104,7 @@ pub struct LimineModuleRequest {
} }
unsafe impl Sync for LimineModuleRequest {} unsafe impl Sync for LimineModuleRequest {}
#[repr(C)] #[repr(C, align(8))]
pub struct LimineSmpInfo { pub struct LimineSmpInfo {
pub processor_id: u32, pub processor_id: u32,
pub lapic_id: u32, pub lapic_id: u32,
@@ -116,7 +113,7 @@ pub struct LimineSmpInfo {
pub extra_argument: u64, pub extra_argument: u64,
} }
#[repr(C)] #[repr(C, align(8))]
pub struct LimineSmpResponse { pub struct LimineSmpResponse {
pub revision: u64, pub revision: u64,
pub flags: u32, pub flags: u32,
@@ -125,7 +122,7 @@ pub struct LimineSmpResponse {
pub cpus: *const *const LimineSmpInfo, pub cpus: *const *const LimineSmpInfo,
} }
#[repr(C)] #[repr(C, align(8))]
pub struct LimineSmpRequest { pub struct LimineSmpRequest {
pub id: [u64; 4], pub id: [u64; 4],
pub revision: u64, pub revision: u64,
@@ -134,57 +131,90 @@ pub struct LimineSmpRequest {
} }
unsafe impl Sync for LimineSmpRequest {} unsafe impl Sync for LimineSmpRequest {}
/* Constants for Limine Request Identifiers */ // =============================================================================
#[used] // Limine Protocol Markers & Requests (all in writable sections)
#[link_section = ".limine_requests"] // =============================================================================
pub static BASE_REVISION: LimineBaseRevision = LimineBaseRevision {
id: [0xf9562b2d5c95a6c8, 0x6a59638edd43477b, 0, 3],
revision: 3,
};
/// Base Revision tag - bootloader sets [2] to 0 when supported
#[used] #[used]
#[no_mangle]
#[link_section = ".limine_base_revision"]
pub static mut limine_base_revision: [u64; 3] = [
0xf9562b2d5c95a6c8,
0x6a7b384944536bdc,
0, // Base revision 0 - widest compatibility
];
/// Start sentinel marker for Limine request scan
#[used]
#[no_mangle]
#[link_section = ".limine_requests_start"]
pub static limine_requests_start_marker: [u64; 4] = [
0xf6b8f4b39de7d1ae, 0xfab91a6940fcb9cf,
0x785c6ed015d3e316, 0x181e920a7852b9d9,
];
/// HHDM Request (Higher-Half Direct Map offset)
#[used]
#[no_mangle]
#[link_section = ".limine_requests"] #[link_section = ".limine_requests"]
pub static HHDM_REQUEST: LimineHhdmRequest = LimineHhdmRequest { pub static mut HHDM_REQUEST: LimineHhdmRequest = LimineHhdmRequest {
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x48d150213051ecda, 0x21e2d0708170e71e], id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x48dcf1cb8ad2b852, 0x63984e959a98244b],
revision: 0, revision: 0,
response: core::ptr::null(), response: core::ptr::null(),
}; };
/// Memory Map Request
#[used] #[used]
#[no_mangle]
#[link_section = ".limine_requests"] #[link_section = ".limine_requests"]
pub static MEMMAP_REQUEST: LimineMemmapRequest = LimineMemmapRequest { pub static mut MEMMAP_REQUEST: LimineMemmapRequest = LimineMemmapRequest {
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x67cf3d9d378a14e6, 0xe310ac092f613308], id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62],
revision: 0, revision: 0,
response: core::ptr::null(), response: core::ptr::null(),
}; };
/// Kernel Virtual/Physical Address Request
#[used] #[used]
#[no_mangle]
#[link_section = ".limine_requests"] #[link_section = ".limine_requests"]
pub static KERNEL_ADDRESS_REQUEST: LimineKernelAddressRequest = LimineKernelAddressRequest { pub static mut KERNEL_ADDRESS_REQUEST: LimineKernelAddressRequest = LimineKernelAddressRequest {
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x71ba76863cc55f63, 0xb2644a48c516a487], id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x71ba76863cc55f63, 0xb2644a48c516a487],
revision: 0, revision: 0,
response: core::ptr::null(), response: core::ptr::null(),
}; };
/// Boot Module Request
#[used] #[used]
#[no_mangle]
#[link_section = ".limine_requests"] #[link_section = ".limine_requests"]
pub static MODULE_REQUEST: LimineModuleRequest = LimineModuleRequest { pub static mut MODULE_REQUEST: LimineModuleRequest = LimineModuleRequest {
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x3e7e2797022b3699, 0xa69dc94525d978b1], id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x3e7e279702be32af, 0xca1c4f3bd1280cee],
revision: 0, revision: 0,
response: core::ptr::null(), response: core::ptr::null(),
internal_module_count: 0, internal_module_count: 0,
internal_modules: core::ptr::null(), internal_modules: core::ptr::null(),
}; };
/// SMP Multicore Request
#[used] #[used]
#[no_mangle]
#[link_section = ".limine_requests"] #[link_section = ".limine_requests"]
pub static SMP_REQUEST: LimineSmpRequest = LimineSmpRequest { pub static mut SMP_REQUEST: LimineSmpRequest = LimineSmpRequest {
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x95a105da0e6d6305, 0xa5951e16b0f99bc7], id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0],
revision: 0, revision: 0,
response: core::ptr::null(), response: core::ptr::null(),
flags: 0, flags: 0,
}; };
/// End sentinel marker
#[used]
#[no_mangle]
#[link_section = ".limine_requests_end"]
pub static limine_requests_end_marker: [u64; 2] = [
0xadc0e0531bb10d03, 0x9572709f31764c62,
];
/// Get HHDM direct map virtual offset /// Get HHDM direct map virtual offset
pub fn get_hhdm_offset() -> Option<u64> { pub fn get_hhdm_offset() -> Option<u64> {
unsafe { unsafe {
+21 -12
View File
@@ -18,25 +18,28 @@ core::arch::global_asm!(include_str!("../asm/context.S"));
core::arch::global_asm!(include_str!("../asm/syscall_entry.S")); core::arch::global_asm!(include_str!("../asm/syscall_entry.S"));
core::arch::global_asm!(include_str!("../asm/ring3_enter.S")); core::arch::global_asm!(include_str!("../asm/ring3_enter.S"));
core::arch::global_asm!(include_str!("../asm/timer_isr.S")); core::arch::global_asm!(include_str!("../asm/timer_isr.S"));
core::arch::global_asm!(include_str!("../asm/keyboard_isr.S"));
#[no_mangle] #[no_mangle]
pub extern "C" fn _start() -> ! { pub extern "C" fn _start() -> ! {
// 1. Validate Limine Base Revision // 1. Early Serial Initialization
if BASE_REVISION.revision != 3 {
loop { core::hint::spin_loop(); }
}
// 2. Early Serial Initialization
drivers::serial::SerialPort::init(); drivers::serial::SerialPort::init();
kprintln!("\n======================================================="); kprintln!("\n=======================================================");
kprintln!(" opencoreC Microkernel v0.1.0 (x86_64)"); kprintln!(" opencoreC Microkernel v0.1.0 (x86_64)");
kprintln!("======================================================="); kprintln!("=======================================================");
// 2. Validate Limine Base Revision
unsafe {
if limine_base_revision[2] != 0 {
kprintln!("[WARN] Limine Base Revision 3 not supported by bootloader.");
}
}
// 3. Obtain Limine responses // 3. Obtain Limine responses
let hhdm_offset = get_hhdm_offset().expect("Limine HHDM response missing"); let hhdm_offset = get_hhdm_offset().expect("Limine HHDM response missing");
let memmap_resp = MEMMAP_REQUEST.response; let memmap_resp = unsafe { MEMMAP_REQUEST.response };
let kernel_addr_resp = KERNEL_ADDRESS_REQUEST.response; let kernel_addr_resp = unsafe { KERNEL_ADDRESS_REQUEST.response };
kprintln!("[BOOT] Limine HHDM Base Virtual Address: {:#018x}", hhdm_offset); kprintln!("[BOOT] Limine HHDM Base Virtual Address: {:#018x}", hhdm_offset);
if !kernel_addr_resp.is_null() { if !kernel_addr_resp.is_null() {
@@ -47,9 +50,14 @@ pub extern "C" fn _start() -> ! {
); );
} }
} }
unsafe {
kprintln!("[DBG] MEMMAP resp ptr: {:#018x}", MEMMAP_REQUEST.response as u64);
kprintln!("[DBG] MODULE resp ptr: {:#018x}", MODULE_REQUEST.response as u64);
kprintln!("[DBG] SMP resp ptr: {:#018x}", SMP_REQUEST.response as u64);
}
// 4. Initialize Architecture (GDT, IDT, Syscall MSRs) // 4. Initialize Architecture (GDT, IDT, Syscall MSRs, SMP)
kprintln!("[BOOT] Initializing GDT, IDT, and Fast-Path Syscall MSRs..."); kprintln!("[BOOT] Initializing GDT, IDT, Fast-Path Syscall MSRs & SMP...");
unsafe { unsafe {
arch::init(); arch::init();
} }
@@ -65,6 +73,7 @@ pub extern "C" fn _start() -> ! {
kprintln!("[BOOT] Configuring PIT 8254 timer for Preemptive Multi-Tasking..."); kprintln!("[BOOT] Configuring PIT 8254 timer for Preemptive Multi-Tasking...");
unsafe { unsafe {
drivers::timer::init(100); drivers::timer::init(100);
drivers::keyboard::init();
} }
// 7. Initialize IPC & Capability System // 7. Initialize IPC & Capability System
@@ -88,6 +97,7 @@ pub extern "C" fn _start() -> ! {
let name = match i { let name = match i {
0 => "init_server", 0 => "init_server",
1 => "uart_driver", 1 => "uart_driver",
2 => "sh",
_ => "user_server", _ => "user_server",
}; };
@@ -118,8 +128,7 @@ pub extern "C" fn _start() -> ! {
} }
} }
kprintln!("[KERNEL] Entering idle loop.\n"); // Idle loop: wait for interrupts and let threads execute
loop { loop {
unsafe { unsafe {
core::arch::asm!("sti; hlt"); core::arch::asm!("sti; hlt");
+4 -1
View File
@@ -6,9 +6,12 @@ use crate::kprintln;
pub unsafe fn init(memmap: *const LimineMemmapResponse) { pub unsafe fn init(memmap: *const LimineMemmapResponse) {
let hhdm = get_hhdm_offset().expect("Limine HHDM is required"); let hhdm = get_hhdm_offset().expect("Limine HHDM is required");
kprintln!("[MM] HHDM OK: {:#018x}, memmap ptr: {:#018x}", hhdm, memmap as u64);
let pfa_ptr = core::ptr::addr_of_mut!(pfa::PFA); let pfa_ptr = core::ptr::addr_of_mut!(pfa::PFA);
let vmm_ptr = core::ptr::addr_of_mut!(vmm::VMM); kprintln!("[MM] Calling PFA init...");
(*pfa_ptr).init(memmap, hhdm); (*pfa_ptr).init(memmap, hhdm);
kprintln!("[MM] PFA init complete. Calling VMM init...");
let vmm_ptr = core::ptr::addr_of_mut!(vmm::VMM);
(*vmm_ptr).init(hhdm); (*vmm_ptr).init(hhdm);
kprintln!("[MM] Memory Management Subsystem Initialized."); kprintln!("[MM] Memory Management Subsystem Initialized.");
} }
+11 -4
View File
@@ -29,28 +29,33 @@ impl FrameAllocator {
pub unsafe fn init(&mut self, memmap: *const LimineMemmapResponse, hhdm: u64) { pub unsafe fn init(&mut self, memmap: *const LimineMemmapResponse, hhdm: u64) {
self.hhdm_offset = hhdm; self.hhdm_offset = hhdm;
if memmap.is_null() { if memmap.is_null() {
kprintln!("[PFA] ERROR: memmap is null!");
return; return;
} }
let entry_count = (*memmap).entry_count as usize; let entry_count = (*memmap).entry_count as usize;
let entries = (*memmap).entries; let entries_ptr = (*memmap).entries;
kprintln!("[PFA] entry_count={}, entries_ptr={:#018x}", entry_count, entries_ptr as u64);
let mut max_paddr: u64 = 0; let mut max_paddr: u64 = 0;
for i in 0..entry_count { for i in 0..entry_count {
let entry = *entries.add(i); let entry = *entries_ptr.add(i);
let end = (*entry).base + (*entry).length; let end = (*entry).base + (*entry).length;
kprintln!("[PFA] entry[{}]: base={:#x} len={:#x} type={}", i, (*entry).base, (*entry).length, (*entry).typ);
if end > max_paddr { if end > max_paddr {
max_paddr = end; max_paddr = end;
} }
} }
kprintln!("[PFA] max_paddr={:#x}", max_paddr);
self.total_frames = (max_paddr as usize) / PAGE_SIZE; self.total_frames = (max_paddr as usize) / PAGE_SIZE;
self.bitmap_size_bytes = (self.total_frames + 7) / 8; self.bitmap_size_bytes = (self.total_frames + 7) / 8;
kprintln!("[PFA] total_frames={}, bitmap_size={} bytes", self.total_frames, self.bitmap_size_bytes);
// Find a usable region large enough to place the bitmap // Find a usable region large enough to place the bitmap
let mut bitmap_paddr: u64 = 0; let mut bitmap_paddr: u64 = 0;
for i in 0..entry_count { for i in 0..entry_count {
let entry = *entries.add(i); let entry = *entries_ptr.add(i);
if (*entry).typ == LimineMemoryType::Usable as u64 if (*entry).typ == LimineMemoryType::Usable as u64
&& (*entry).length >= self.bitmap_size_bytes as u64 && (*entry).length >= self.bitmap_size_bytes as u64
{ {
@@ -63,13 +68,15 @@ impl FrameAllocator {
kprintln!("[FATAL] Could not find memory for PFA bitmap!"); kprintln!("[FATAL] Could not find memory for PFA bitmap!");
return; return;
} }
kprintln!("[PFA] Bitmap at phys={:#x}, virt={:#x}", bitmap_paddr, bitmap_paddr + hhdm);
self.bitmap = (bitmap_paddr + hhdm) as *mut u8; self.bitmap = (bitmap_paddr + hhdm) as *mut u8;
core::ptr::write_bytes(self.bitmap, 0xFF, self.bitmap_size_bytes); core::ptr::write_bytes(self.bitmap, 0xFF, self.bitmap_size_bytes);
kprintln!("[PFA] Bitmap zeroed OK");
self.free_frames = 0; self.free_frames = 0;
for i in 0..entry_count { for i in 0..entry_count {
let entry = *entries.add(i); let entry = *entries_ptr.add(i);
if (*entry).typ == LimineMemoryType::Usable as u64 { if (*entry).typ == LimineMemoryType::Usable as u64 {
let start_frame = ((*entry).base as usize) / PAGE_SIZE; let start_frame = ((*entry).base as usize) / PAGE_SIZE;
let count = ((*entry).length as usize) / PAGE_SIZE; let count = ((*entry).length as usize) / PAGE_SIZE;
+19 -72
View File
@@ -9,6 +9,7 @@ use crate::kprintln;
extern "C" { extern "C" {
fn switch_to(prev_rsp: *mut u64, next_rsp: u64); fn switch_to(prev_rsp: *mut u64, next_rsp: u64);
fn thread_entry_trampoline();
} }
const DEFAULT_QUANTUM_TICKS: u32 = 5; // 5 ticks @ 100Hz = 50ms time slice const DEFAULT_QUANTUM_TICKS: u32 = 5; // 5 ticks @ 100Hz = 50ms time slice
@@ -62,31 +63,21 @@ impl Scheduler {
self.threads[i].user_rsp = user_rsp; self.threads[i].user_rsp = user_rsp;
self.threads[i].user_rip = entry_rip; self.threads[i].user_rip = entry_rip;
// Build initial interrupt frame for timer_isr_entry / iretq return: // Build initial kernel stack frame for new thread:
// [kstack - 5]: SS (0x1B) // switch_to will pop r15, r14, r13, r12, rbx, rbp, then ret.
// [kstack - 4]: RSP (user_rsp) // We configure r13 = entry_rip, r12 = user_rsp, ret = thread_entry_trampoline.
// [kstack - 3]: RFLAGS (0x202) unsafe {
// [kstack - 2]: CS (0x23) let k = kernel_stack_top as *mut u64;
// [kstack - 1]: RIP (entry_rip) *k.sub(1) = thread_entry_trampoline as u64; // ret address
// [kstack - 20..-6]: 15 GPRs (rax, rbx, rcx, rdx, rsi, rdi, rbp, r8..r15) *k.sub(2) = 0; // rbp
unsafe { *k.sub(3) = 0; // rbx
let kstack = kernel_stack_top as *mut u64; *k.sub(4) = user_rsp; // r12 -> user_rsp
let frame_ptr = kstack.sub(20); *k.sub(5) = entry_rip; // r13 -> entry_rip
*k.sub(6) = 0; // r14
*k.sub(7) = 0; // r15
// GPRs (all 0) self.threads[i].context.rsp = k.sub(7) as u64;
for j in 0..15 { }
*frame_ptr.add(j) = 0;
}
// IRETQ Frame
*frame_ptr.add(15) = entry_rip; // RIP
*frame_ptr.add(16) = 0x23; // CS (User Code)
*frame_ptr.add(17) = 0x0202; // RFLAGS (IF enabled)
*frame_ptr.add(18) = user_rsp; // RSP (User Stack)
*frame_ptr.add(19) = 0x1B; // SS (User Data)
self.threads[i].context.rsp = frame_ptr as u64;
}
kprintln!("[SCHED] Created Preemptible Thread [TID {}] '{}' (RIP: {:#x}, RSP: {:#x})", kprintln!("[SCHED] Created Preemptible Thread [TID {}] '{}' (RIP: {:#x}, RSP: {:#x})",
tid, name, entry_rip, user_rsp); tid, name, entry_rip, user_rsp);
@@ -131,61 +122,17 @@ impl Scheduler {
} }
/// Preemptive tick called directly from timer ISR handler /// Preemptive tick called directly from timer ISR handler
pub fn preempt(&mut self, saved_rsp: u64) -> u64 { pub fn preempt_tick(&mut self) {
self.ticks += 1; self.ticks += 1;
if self.quantum_remaining > 0 { if self.quantum_remaining > 0 {
self.quantum_remaining -= 1; self.quantum_remaining -= 1;
if self.quantum_remaining > 0 {
return saved_rsp; // Keep running current thread
}
} }
// Time slice quantum expired -> Preempt current thread! if self.quantum_remaining == 0 {
self.quantum_remaining = DEFAULT_QUANTUM_TICKS; self.quantum_remaining = DEFAULT_QUANTUM_TICKS;
self.schedule();
let prev_idx = self.current_tid;
self.threads[prev_idx].context.rsp = saved_rsp;
if self.threads[prev_idx].state == ThreadState::Running {
self.threads[prev_idx].state = ThreadState::Ready;
} }
// Find next Ready thread (Round-Robin)
let mut next_idx = (prev_idx + 1) % MAX_THREADS;
let mut found = false;
for _ in 0..MAX_THREADS {
if self.threads[next_idx].state == ThreadState::Ready {
found = true;
break;
}
next_idx = (next_idx + 1) % MAX_THREADS;
}
if !found {
// Restore previous thread if it's still runnable
if self.threads[prev_idx].state == ThreadState::Ready {
self.threads[prev_idx].state = ThreadState::Running;
}
return saved_rsp;
}
self.current_tid = next_idx;
self.threads[next_idx].state = ThreadState::Running;
let next_pml4 = self.threads[next_idx].pml4_paddr;
let next_kstack = self.threads[next_idx].kernel_stack_top;
let next_rsp = self.threads[next_idx].context.rsp;
unsafe {
if next_pml4 != 0 {
(*core::ptr::addr_of_mut!(VMM)).load_cr3(next_pml4);
}
if next_kstack != 0 {
set_kernel_stack(next_kstack);
}
}
next_rsp
} }
pub fn schedule(&mut self) { pub fn schedule(&mut self) {
+1 -1
View File
@@ -1,5 +1,5 @@
# Limine Bootloader Configuration (Legacy Alias) # Limine Bootloader Configuration (Legacy Alias)
TIMEOUT=3 TIMEOUT=0
:opencoreC Microkernel (x86_64) :opencoreC Microkernel (x86_64)
PROTOCOL=limine PROTOCOL=limine
+1 -1
View File
@@ -1,5 +1,5 @@
# Limine Bootloader Configuration for opencoreC Microkernel # Limine Bootloader Configuration for opencoreC Microkernel
timeout: 3 timeout: 0
default_entry: 1 default_entry: 1
/opencoreC Microkernel (x86_64) /opencoreC Microkernel (x86_64)
+5
View File
@@ -202,6 +202,11 @@ static void handle_command(char *cmd) {
} }
extern "C" void _start() { extern "C" void _start() {
// Let root servers finish early boot logging before taking over the terminal
for (int i = 0; i < 6; i++) {
sys_yield();
}
sys_log_debug("\033[2J\033[H", 7); sys_log_debug("\033[2J\033[H", 7);
puts("==============================================================================="); puts("===============================================================================");
puts(" opencoreC High-Performance Microkernel Operating System (x86_64) "); puts(" opencoreC High-Performance Microkernel Operating System (x86_64) ");