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:
@@ -7,6 +7,7 @@
|
||||
*.bin
|
||||
*.iso
|
||||
*.img
|
||||
/boot/
|
||||
|
||||
# Cargo
|
||||
Cargo.lock
|
||||
|
||||
@@ -18,11 +18,12 @@ LIB_DIR := lib
|
||||
HAL_DIR := hal
|
||||
SERVERS_DIR := servers
|
||||
KERNEL_DIR := kernel
|
||||
ISO_DIR := $(BUILD_DIR)/iso_root
|
||||
LIMINE_DIR := boot/limine
|
||||
|
||||
# Common Freestanding Flags
|
||||
COMMON_FLAGS := -Wall -Wextra -O2 -ffreestanding -fno-stack-protector \
|
||||
-fno-pie -no-pie -mno-red-zone -m64 \
|
||||
-mno-sse -mno-sse2 -mno-avx \
|
||||
-I$(INCLUDE_DIR) -I$(LIB_DIR)/libc/include
|
||||
|
||||
CFLAGS := $(COMMON_FLAGS) -std=c11
|
||||
@@ -35,18 +36,20 @@ INIT_SERVER := $(BUILD_DIR)/init_server.elf
|
||||
UART_DRIVER := $(BUILD_DIR)/uart_driver.elf
|
||||
SH_SERVER := $(BUILD_DIR)/sh.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
|
||||
|
||||
.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
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
@cp $(KERNEL_BIN) $(KERNEL_ELF)
|
||||
@echo "======================================================="
|
||||
@echo " [SUCCESS] opencoreC Skeleton Build Complete!"
|
||||
@echo " [SUCCESS] opencoreC Build Complete!"
|
||||
@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 "======================================================="
|
||||
|
||||
kernel:
|
||||
@@ -90,25 +93,60 @@ servers: libc hal
|
||||
@$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(PROCMGR) \
|
||||
$(BUILD_DIR)/procmgr_main.o $(BUILD_DIR)/ipc.o $(LIBC_OBJS)
|
||||
|
||||
# Staging Limine Boot ISO directory
|
||||
iso: all
|
||||
@mkdir -p $(ISO_DIR)/boot $(ISO_DIR)/EFI/BOOT
|
||||
@cp $(KERNEL_ELF) $(ISO_DIR)/boot/
|
||||
@cp $(INIT_SERVER) $(ISO_DIR)/boot/
|
||||
@cp $(UART_DRIVER) $(ISO_DIR)/boot/
|
||||
@cp $(SH_SERVER) $(ISO_DIR)/boot/
|
||||
@cp limine.conf $(ISO_DIR)/boot/limine.conf
|
||||
@cp limine.cfg $(ISO_DIR)/boot/limine.cfg
|
||||
@echo "[ISO] Staged boot files in $(ISO_DIR)/"
|
||||
# Download and build the Limine bootloader host tool
|
||||
limine-setup:
|
||||
@if [ ! -d "$(LIMINE_DIR)" ]; then \
|
||||
echo "[LIMINE] Cloning Limine v8 binary branch..."; \
|
||||
git clone https://github.com/limine-bootloader/limine.git --branch=v8.x-binary --depth=1 $(LIMINE_DIR); \
|
||||
fi
|
||||
@echo "[LIMINE] Building host installer tool..."
|
||||
@$(MAKE) -C $(LIMINE_DIR) limine
|
||||
|
||||
run: all
|
||||
@echo "[QEMU] Launching opencoreC in QEMU (SMP 4 Cores, Serial stdio)..."
|
||||
@$(QEMU) -M q35 -smp 4 -m 512M \
|
||||
-serial stdio \
|
||||
-display none \
|
||||
# Build a 64 MiB bootable FAT32 disk image with Limine BIOS/UEFI
|
||||
image: all limine-setup
|
||||
@echo "[IMAGE] Creating bootable disk image (64 MiB FAT32)..."
|
||||
@dd if=/dev/zero of=$(DISK_IMG) bs=1M count=64 2>/dev/null
|
||||
@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-shutdown \
|
||||
-kernel $(KERNEL_ELF)
|
||||
-display none \
|
||||
-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:
|
||||
@echo "[CLEAN] Removing build directory and cargo target..."
|
||||
@@ -116,7 +154,16 @@ clean:
|
||||
|
||||
help:
|
||||
@echo "opencoreC Microkernel Build System"
|
||||
@echo ""
|
||||
@echo "Targets:"
|
||||
@echo " make all - Build kernel, HAL, libc, and all user servers"
|
||||
@echo " make run - Launch QEMU with SMP 4 Cores & Serial output"
|
||||
@echo " make clean - Clean build artifacts"
|
||||
@echo " make all - Build kernel, HAL, libc, and all user servers"
|
||||
@echo " make image - Create bootable disk image (build/opencoreC.img)"
|
||||
@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
@@ -25,15 +25,25 @@ SECTIONS
|
||||
*(.rodata .rodata.*)
|
||||
} :rodata
|
||||
|
||||
/* Limine requests section */
|
||||
.limine_requests : {
|
||||
KEEP(*(.limine_requests_start))
|
||||
KEEP(*(.limine_requests))
|
||||
KEEP(*(.limine_requests_end))
|
||||
} :rodata
|
||||
|
||||
. = 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
|
||||
|
||||
+51
-8
@@ -1,14 +1,15 @@
|
||||
/* opencoreC - Thread Context Switch */
|
||||
/* opencoreC - Thread Context Switch & Entry Trampoline */
|
||||
.global switch_to
|
||||
.global thread_entry_trampoline
|
||||
|
||||
.section .text
|
||||
/*
|
||||
* void switch_to(uint64_t **prev_rsp_ptr, uint64_t *next_rsp);
|
||||
* RDI = pointer to current thread's stored RSP
|
||||
* RSI = target thread's RSP
|
||||
* switch_to(uint64_t *prev_rsp_ptr, uint64_t next_rsp)
|
||||
* RDI = ¤t_thread.context.rsp (save current RSP here)
|
||||
* RSI = next thread's saved RSP
|
||||
*/
|
||||
switch_to:
|
||||
/* Push callee-saved registers of outgoing thread */
|
||||
/* Save callee-saved registers of outgoing thread */
|
||||
push rbp
|
||||
push rbx
|
||||
push r12
|
||||
@@ -16,13 +17,13 @@ switch_to:
|
||||
push r14
|
||||
push r15
|
||||
|
||||
/* Save current stack pointer */
|
||||
/* Save RSP of outgoing thread */
|
||||
mov [rdi], rsp
|
||||
|
||||
/* Load target stack pointer */
|
||||
/* Load RSP of incoming thread */
|
||||
mov rsp, rsi
|
||||
|
||||
/* Pop callee-saved registers of incoming thread */
|
||||
/* Restore callee-saved registers of incoming thread */
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
@@ -31,3 +32,45 @@ switch_to:
|
||||
pop rbp
|
||||
|
||||
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
@@ -2,25 +2,46 @@
|
||||
.global syscall_entry
|
||||
.extern kernel_syscall_dispatcher
|
||||
|
||||
/* Per-CPU scratch: user RSP saved here on syscall entry */
|
||||
.section .bss
|
||||
.align 16
|
||||
user_scratch_rsp:
|
||||
.quad 0
|
||||
kernel_temp_stack:
|
||||
.skip 4096
|
||||
kernel_temp_stack_top:
|
||||
.align 8
|
||||
.global cpu0_scratch_user_rsp
|
||||
cpu0_scratch_user_rsp: .quad 0
|
||||
|
||||
.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:
|
||||
/* Save user stack pointer */
|
||||
mov [rip + user_scratch_rsp], rsp
|
||||
lea rsp, [rip + kernel_temp_stack_top]
|
||||
/* Save user RSP */
|
||||
mov [rip + cpu0_scratch_user_rsp], rsp
|
||||
|
||||
/* 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 r11
|
||||
|
||||
/* Save argument and volatile registers to construct SyscallRegisters */
|
||||
/* Push syscall arguments as SyscallRegisters */
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
@@ -29,11 +50,10 @@ syscall_entry:
|
||||
push r9
|
||||
push r10
|
||||
|
||||
/* Pass pointer to SyscallRegisters struct in RDI */
|
||||
mov rdi, rsp
|
||||
call kernel_syscall_dispatcher
|
||||
|
||||
/* Restore registers (rax, rdx, r8 may have return values from dispatcher) */
|
||||
/* Restore args */
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
@@ -42,12 +62,11 @@ syscall_entry:
|
||||
pop rdi
|
||||
pop rax
|
||||
|
||||
/* Restore user RIP and RFLAGS */
|
||||
/* Restore user context */
|
||||
pop r11
|
||||
pop rcx
|
||||
|
||||
/* Restore user stack */
|
||||
mov rsp, [rip + user_scratch_rsp]
|
||||
/* Restore user RSP */
|
||||
mov rsp, [rip + cpu0_scratch_user_rsp]
|
||||
|
||||
/* Return to Ring 3 */
|
||||
sysretq
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
.section .text
|
||||
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 rbx
|
||||
push rcx
|
||||
@@ -21,14 +21,10 @@ timer_isr_entry:
|
||||
push r14
|
||||
push r15
|
||||
|
||||
/* 2. Pass current saved stack pointer in RDI */
|
||||
mov rdi, rsp
|
||||
/* 2. Call handler in Rust (sends EOI and invokes sched.preempt_tick) */
|
||||
call timer_interrupt_handler
|
||||
|
||||
/* 3. Switch stack pointer to returned value (RAX contains next thread's RSP) */
|
||||
mov rsp, rax
|
||||
|
||||
/* 4. Restore all general-purpose registers */
|
||||
/* 3. Restore all general-purpose registers */
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
@@ -45,5 +41,5 @@ timer_isr_entry:
|
||||
pop rbx
|
||||
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
|
||||
|
||||
@@ -37,6 +37,7 @@ struct GdtDescriptor {
|
||||
base: u64,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub static mut TSS: TaskStateSegment = TaskStateSegment::new();
|
||||
|
||||
// 8 entries: Null, KCode, KData, UData, UCode, TSS Low, TSS High, Null
|
||||
|
||||
+123
-26
@@ -55,37 +55,93 @@ extern "C" {
|
||||
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!(
|
||||
// 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",
|
||||
"default_isr_stub:",
|
||||
"push rax",
|
||||
"push rcx",
|
||||
"push rdx",
|
||||
"push rsi",
|
||||
"push rdi",
|
||||
"push r8",
|
||||
"push r9",
|
||||
"push r10",
|
||||
"push r11",
|
||||
"push 0", // dummy error code
|
||||
"push 255", // dummy vector
|
||||
"jmp default_isr_common",
|
||||
|
||||
".global default_isr_common",
|
||||
"default_isr_common:",
|
||||
// Stack at this point (top to bottom):
|
||||
// RSP+0: vector
|
||||
// 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",
|
||||
"call raw_exception_handler",
|
||||
"pop r11",
|
||||
"pop r10",
|
||||
"pop r9",
|
||||
"pop r8",
|
||||
"pop rdi",
|
||||
"pop rsi",
|
||||
"pop rdx",
|
||||
"pop rcx",
|
||||
"pop rax",
|
||||
"pop r11", "pop r10", "pop r9", "pop r8",
|
||||
"pop rdi", "pop rsi",
|
||||
"pop rdx", "pop rcx", "pop rax",
|
||||
// skip vector + error_code from stack
|
||||
"add rsp, 16",
|
||||
"iretq"
|
||||
);
|
||||
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -105,16 +161,57 @@ pub unsafe fn init() {
|
||||
core::arch::asm!("lidt [{0}]", in(reg) &descriptor, options(preserves_flags));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn raw_exception_handler(stack_ptr: *const u64) {
|
||||
kprintln!("[CPU EXCEPTION] Trap triggered at stack frame: {:p}", stack_ptr);
|
||||
#[repr(C)]
|
||||
pub struct ExceptionFrame {
|
||||
// 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]
|
||||
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 {
|
||||
send_eoi();
|
||||
let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER);
|
||||
sched.preempt(saved_rsp)
|
||||
sched.preempt_tick();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,28 @@ pub mod syscall;
|
||||
pub mod smp;
|
||||
|
||||
pub unsafe fn init() {
|
||||
enable_sse();
|
||||
gdt::init();
|
||||
idt::init();
|
||||
syscall::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));
|
||||
}
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
//! Limine Bootloader Protocol Requests & Types for opencoreC Microkernel
|
||||
//! Implements Base Revision 3 specification.
|
||||
//! Implements Limine v8 Base Revision 0 specification.
|
||||
|
||||
#[repr(C)]
|
||||
pub struct LimineBaseRevision {
|
||||
pub id: [u64; 4],
|
||||
pub revision: u64,
|
||||
}
|
||||
unsafe impl Sync for LimineBaseRevision {}
|
||||
#[repr(transparent)]
|
||||
pub struct SyncPtr(pub *const ());
|
||||
unsafe impl Sync for SyncPtr {}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineHhdmResponse {
|
||||
pub revision: u64,
|
||||
pub offset: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineHhdmRequest {
|
||||
pub id: [u64; 4],
|
||||
pub revision: u64,
|
||||
@@ -35,21 +32,21 @@ pub enum LimineMemoryType {
|
||||
Framebuffer = 7,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineMemmapEntry {
|
||||
pub base: u64,
|
||||
pub length: u64,
|
||||
pub typ: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineMemmapResponse {
|
||||
pub revision: u64,
|
||||
pub entry_count: u64,
|
||||
pub entries: *const *const LimineMemmapEntry,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineMemmapRequest {
|
||||
pub id: [u64; 4],
|
||||
pub revision: u64,
|
||||
@@ -57,14 +54,14 @@ pub struct LimineMemmapRequest {
|
||||
}
|
||||
unsafe impl Sync for LimineMemmapRequest {}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineKernelAddressResponse {
|
||||
pub revision: u64,
|
||||
pub physical_base: u64,
|
||||
pub virtual_base: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineKernelAddressRequest {
|
||||
pub id: [u64; 4],
|
||||
pub revision: u64,
|
||||
@@ -72,7 +69,7 @@ pub struct LimineKernelAddressRequest {
|
||||
}
|
||||
unsafe impl Sync for LimineKernelAddressRequest {}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineFile {
|
||||
pub revision: u64,
|
||||
pub address: *const u8,
|
||||
@@ -90,14 +87,14 @@ pub struct LimineFile {
|
||||
pub part_uuid: [u8; 16],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineModuleResponse {
|
||||
pub revision: u64,
|
||||
pub module_count: u64,
|
||||
pub modules: *const *const LimineFile,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineModuleRequest {
|
||||
pub id: [u64; 4],
|
||||
pub revision: u64,
|
||||
@@ -107,7 +104,7 @@ pub struct LimineModuleRequest {
|
||||
}
|
||||
unsafe impl Sync for LimineModuleRequest {}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineSmpInfo {
|
||||
pub processor_id: u32,
|
||||
pub lapic_id: u32,
|
||||
@@ -116,7 +113,7 @@ pub struct LimineSmpInfo {
|
||||
pub extra_argument: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineSmpResponse {
|
||||
pub revision: u64,
|
||||
pub flags: u32,
|
||||
@@ -125,7 +122,7 @@ pub struct LimineSmpResponse {
|
||||
pub cpus: *const *const LimineSmpInfo,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(C, align(8))]
|
||||
pub struct LimineSmpRequest {
|
||||
pub id: [u64; 4],
|
||||
pub revision: u64,
|
||||
@@ -134,57 +131,90 @@ pub struct LimineSmpRequest {
|
||||
}
|
||||
unsafe impl Sync for LimineSmpRequest {}
|
||||
|
||||
/* Constants for Limine Request Identifiers */
|
||||
#[used]
|
||||
#[link_section = ".limine_requests"]
|
||||
pub static BASE_REVISION: LimineBaseRevision = LimineBaseRevision {
|
||||
id: [0xf9562b2d5c95a6c8, 0x6a59638edd43477b, 0, 3],
|
||||
revision: 3,
|
||||
};
|
||||
// =============================================================================
|
||||
// Limine Protocol Markers & Requests (all in writable sections)
|
||||
// =============================================================================
|
||||
|
||||
/// Base Revision tag - bootloader sets [2] to 0 when supported
|
||||
#[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"]
|
||||
pub static HHDM_REQUEST: LimineHhdmRequest = LimineHhdmRequest {
|
||||
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x48d150213051ecda, 0x21e2d0708170e71e],
|
||||
pub static mut HHDM_REQUEST: LimineHhdmRequest = LimineHhdmRequest {
|
||||
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x48dcf1cb8ad2b852, 0x63984e959a98244b],
|
||||
revision: 0,
|
||||
response: core::ptr::null(),
|
||||
};
|
||||
|
||||
/// Memory Map Request
|
||||
#[used]
|
||||
#[no_mangle]
|
||||
#[link_section = ".limine_requests"]
|
||||
pub static MEMMAP_REQUEST: LimineMemmapRequest = LimineMemmapRequest {
|
||||
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x67cf3d9d378a14e6, 0xe310ac092f613308],
|
||||
pub static mut MEMMAP_REQUEST: LimineMemmapRequest = LimineMemmapRequest {
|
||||
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62],
|
||||
revision: 0,
|
||||
response: core::ptr::null(),
|
||||
};
|
||||
|
||||
/// Kernel Virtual/Physical Address Request
|
||||
#[used]
|
||||
#[no_mangle]
|
||||
#[link_section = ".limine_requests"]
|
||||
pub static KERNEL_ADDRESS_REQUEST: LimineKernelAddressRequest = LimineKernelAddressRequest {
|
||||
pub static mut KERNEL_ADDRESS_REQUEST: LimineKernelAddressRequest = LimineKernelAddressRequest {
|
||||
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x71ba76863cc55f63, 0xb2644a48c516a487],
|
||||
revision: 0,
|
||||
response: core::ptr::null(),
|
||||
};
|
||||
|
||||
/// Boot Module Request
|
||||
#[used]
|
||||
#[no_mangle]
|
||||
#[link_section = ".limine_requests"]
|
||||
pub static MODULE_REQUEST: LimineModuleRequest = LimineModuleRequest {
|
||||
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x3e7e2797022b3699, 0xa69dc94525d978b1],
|
||||
pub static mut MODULE_REQUEST: LimineModuleRequest = LimineModuleRequest {
|
||||
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x3e7e279702be32af, 0xca1c4f3bd1280cee],
|
||||
revision: 0,
|
||||
response: core::ptr::null(),
|
||||
internal_module_count: 0,
|
||||
internal_modules: core::ptr::null(),
|
||||
};
|
||||
|
||||
/// SMP Multicore Request
|
||||
#[used]
|
||||
#[no_mangle]
|
||||
#[link_section = ".limine_requests"]
|
||||
pub static SMP_REQUEST: LimineSmpRequest = LimineSmpRequest {
|
||||
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x95a105da0e6d6305, 0xa5951e16b0f99bc7],
|
||||
pub static mut SMP_REQUEST: LimineSmpRequest = LimineSmpRequest {
|
||||
id: [0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0],
|
||||
revision: 0,
|
||||
response: core::ptr::null(),
|
||||
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
|
||||
pub fn get_hhdm_offset() -> Option<u64> {
|
||||
unsafe {
|
||||
|
||||
+21
-12
@@ -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/ring3_enter.S"));
|
||||
core::arch::global_asm!(include_str!("../asm/timer_isr.S"));
|
||||
core::arch::global_asm!(include_str!("../asm/keyboard_isr.S"));
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
// 1. Validate Limine Base Revision
|
||||
if BASE_REVISION.revision != 3 {
|
||||
loop { core::hint::spin_loop(); }
|
||||
}
|
||||
|
||||
// 2. Early Serial Initialization
|
||||
// 1. Early Serial Initialization
|
||||
drivers::serial::SerialPort::init();
|
||||
|
||||
kprintln!("\n=======================================================");
|
||||
kprintln!(" opencoreC Microkernel v0.1.0 (x86_64)");
|
||||
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
|
||||
let hhdm_offset = get_hhdm_offset().expect("Limine HHDM response missing");
|
||||
let memmap_resp = MEMMAP_REQUEST.response;
|
||||
let kernel_addr_resp = KERNEL_ADDRESS_REQUEST.response;
|
||||
let memmap_resp = unsafe { MEMMAP_REQUEST.response };
|
||||
let kernel_addr_resp = unsafe { KERNEL_ADDRESS_REQUEST.response };
|
||||
|
||||
kprintln!("[BOOT] Limine HHDM Base Virtual Address: {:#018x}", hhdm_offset);
|
||||
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)
|
||||
kprintln!("[BOOT] Initializing GDT, IDT, and Fast-Path Syscall MSRs...");
|
||||
// 4. Initialize Architecture (GDT, IDT, Syscall MSRs, SMP)
|
||||
kprintln!("[BOOT] Initializing GDT, IDT, Fast-Path Syscall MSRs & SMP...");
|
||||
unsafe {
|
||||
arch::init();
|
||||
}
|
||||
@@ -65,6 +73,7 @@ pub extern "C" fn _start() -> ! {
|
||||
kprintln!("[BOOT] Configuring PIT 8254 timer for Preemptive Multi-Tasking...");
|
||||
unsafe {
|
||||
drivers::timer::init(100);
|
||||
drivers::keyboard::init();
|
||||
}
|
||||
|
||||
// 7. Initialize IPC & Capability System
|
||||
@@ -88,6 +97,7 @@ pub extern "C" fn _start() -> ! {
|
||||
let name = match i {
|
||||
0 => "init_server",
|
||||
1 => "uart_driver",
|
||||
2 => "sh",
|
||||
_ => "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 {
|
||||
unsafe {
|
||||
core::arch::asm!("sti; hlt");
|
||||
|
||||
@@ -6,9 +6,12 @@ use crate::kprintln;
|
||||
|
||||
pub unsafe fn init(memmap: *const LimineMemmapResponse) {
|
||||
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 vmm_ptr = core::ptr::addr_of_mut!(vmm::VMM);
|
||||
kprintln!("[MM] Calling PFA init...");
|
||||
(*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);
|
||||
kprintln!("[MM] Memory Management Subsystem Initialized.");
|
||||
}
|
||||
|
||||
+11
-4
@@ -29,28 +29,33 @@ impl FrameAllocator {
|
||||
pub unsafe fn init(&mut self, memmap: *const LimineMemmapResponse, hhdm: u64) {
|
||||
self.hhdm_offset = hhdm;
|
||||
if memmap.is_null() {
|
||||
kprintln!("[PFA] ERROR: memmap is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
for i in 0..entry_count {
|
||||
let entry = *entries.add(i);
|
||||
let entry = *entries_ptr.add(i);
|
||||
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 {
|
||||
max_paddr = end;
|
||||
}
|
||||
}
|
||||
|
||||
kprintln!("[PFA] max_paddr={:#x}", max_paddr);
|
||||
self.total_frames = (max_paddr as usize) / PAGE_SIZE;
|
||||
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
|
||||
let mut bitmap_paddr: u64 = 0;
|
||||
for i in 0..entry_count {
|
||||
let entry = *entries.add(i);
|
||||
let entry = *entries_ptr.add(i);
|
||||
if (*entry).typ == LimineMemoryType::Usable 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!");
|
||||
return;
|
||||
}
|
||||
kprintln!("[PFA] Bitmap at phys={:#x}, virt={:#x}", bitmap_paddr, bitmap_paddr + hhdm);
|
||||
|
||||
self.bitmap = (bitmap_paddr + hhdm) as *mut u8;
|
||||
core::ptr::write_bytes(self.bitmap, 0xFF, self.bitmap_size_bytes);
|
||||
kprintln!("[PFA] Bitmap zeroed OK");
|
||||
|
||||
self.free_frames = 0;
|
||||
for i in 0..entry_count {
|
||||
let entry = *entries.add(i);
|
||||
let entry = *entries_ptr.add(i);
|
||||
if (*entry).typ == LimineMemoryType::Usable as u64 {
|
||||
let start_frame = ((*entry).base as usize) / PAGE_SIZE;
|
||||
let count = ((*entry).length as usize) / PAGE_SIZE;
|
||||
|
||||
+19
-72
@@ -9,6 +9,7 @@ use crate::kprintln;
|
||||
|
||||
extern "C" {
|
||||
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
|
||||
@@ -62,31 +63,21 @@ impl Scheduler {
|
||||
self.threads[i].user_rsp = user_rsp;
|
||||
self.threads[i].user_rip = entry_rip;
|
||||
|
||||
// Build initial interrupt frame for timer_isr_entry / iretq return:
|
||||
// [kstack - 5]: SS (0x1B)
|
||||
// [kstack - 4]: RSP (user_rsp)
|
||||
// [kstack - 3]: RFLAGS (0x202)
|
||||
// [kstack - 2]: CS (0x23)
|
||||
// [kstack - 1]: RIP (entry_rip)
|
||||
// [kstack - 20..-6]: 15 GPRs (rax, rbx, rcx, rdx, rsi, rdi, rbp, r8..r15)
|
||||
unsafe {
|
||||
let kstack = kernel_stack_top as *mut u64;
|
||||
let frame_ptr = kstack.sub(20);
|
||||
// Build initial kernel stack frame for new thread:
|
||||
// switch_to will pop r15, r14, r13, r12, rbx, rbp, then ret.
|
||||
// We configure r13 = entry_rip, r12 = user_rsp, ret = thread_entry_trampoline.
|
||||
unsafe {
|
||||
let k = kernel_stack_top as *mut u64;
|
||||
*k.sub(1) = thread_entry_trampoline as u64; // ret address
|
||||
*k.sub(2) = 0; // rbp
|
||||
*k.sub(3) = 0; // rbx
|
||||
*k.sub(4) = user_rsp; // r12 -> user_rsp
|
||||
*k.sub(5) = entry_rip; // r13 -> entry_rip
|
||||
*k.sub(6) = 0; // r14
|
||||
*k.sub(7) = 0; // r15
|
||||
|
||||
// GPRs (all 0)
|
||||
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;
|
||||
}
|
||||
self.threads[i].context.rsp = k.sub(7) as u64;
|
||||
}
|
||||
|
||||
kprintln!("[SCHED] Created Preemptible Thread [TID {}] '{}' (RIP: {:#x}, RSP: {:#x})",
|
||||
tid, name, entry_rip, user_rsp);
|
||||
@@ -131,61 +122,17 @@ impl Scheduler {
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
if self.quantum_remaining > 0 {
|
||||
self.quantum_remaining -= 1;
|
||||
if self.quantum_remaining > 0 {
|
||||
return saved_rsp; // Keep running current thread
|
||||
}
|
||||
}
|
||||
|
||||
// Time slice quantum expired -> Preempt current thread!
|
||||
self.quantum_remaining = DEFAULT_QUANTUM_TICKS;
|
||||
|
||||
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;
|
||||
if self.quantum_remaining == 0 {
|
||||
self.quantum_remaining = DEFAULT_QUANTUM_TICKS;
|
||||
self.schedule();
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Limine Bootloader Configuration (Legacy Alias)
|
||||
TIMEOUT=3
|
||||
TIMEOUT=0
|
||||
|
||||
:opencoreC Microkernel (x86_64)
|
||||
PROTOCOL=limine
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Limine Bootloader Configuration for opencoreC Microkernel
|
||||
timeout: 3
|
||||
timeout: 0
|
||||
default_entry: 1
|
||||
|
||||
/opencoreC Microkernel (x86_64)
|
||||
|
||||
@@ -202,6 +202,11 @@ static void handle_command(char *cmd) {
|
||||
}
|
||||
|
||||
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);
|
||||
puts("===============================================================================");
|
||||
puts(" opencoreC High-Performance Microkernel Operating System (x86_64) ");
|
||||
|
||||
Reference in New Issue
Block a user