diff --git a/.cargo/config.toml b/.cargo/config.toml index 233ff8a..edc59e4 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,6 +3,11 @@ target = "x86_64-unknown-none" [target.x86_64-unknown-none] runner = "qemu-system-x86_64 -kernel" +rustflags = [ + "-C", "relocation-model=static", + "-C", "link-arg=-Tkernel/linker.ld", + "-C", "link-arg=-no-pie" +] [unstable] build-std = ["core", "alloc"] diff --git a/Makefile b/Makefile index a485452..e397eb3 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ all: kernel modules drivers # Build the core kernel kernel: @echo "Building Rust kernel ($(ARCH), $(BUILD_TYPE))" - cd kernel && $(CARGO) build $(CARGO_FLAGS) --target x86_64-unknown-none -Z build-std=core,alloc + $(CARGO) build $(CARGO_FLAGS) --bin rust-kernel --target x86_64-unknown-none # Build kernel modules modules: $(RUST_MODULES) diff --git a/README.md b/README.md index a890a7b..09b08a5 100644 --- a/README.md +++ b/README.md @@ -315,8 +315,8 @@ perf counters # Show performance counters - **Memory Mapping**: Support for memory-mapped I/O and files ### Process & Task Management -- **Preemptive Scheduling**: Priority-based with round-robin -- **Context Switching**: Full CPU context preservation +- **Preemptive Scheduling**: Priority-based with round-robin and CFS (Completely Fair Scheduler) +- **Context Switching**: Full CPU context preservation including GPRs, segment registers, and control registers - **Kernel Threads**: Lightweight kernel task execution - **Process States**: Running, ready, waiting, zombie states @@ -360,6 +360,7 @@ perf counters # Show performance counters ### 📋 **Future Enhancements** - [ ] SMP (multi-processor) support +- [ ] ACPI (Advanced Configuration and Power Interface) support - [ ] Advanced file systems (ext2, FAT32) - [ ] Complete TCP/IP networking stack - [ ] Graphics and display support diff --git a/build_and_test.sh b/build_and_test.sh index 8202152..5169540 100755 --- a/build_and_test.sh +++ b/build_and_test.sh @@ -8,6 +8,9 @@ set -e # Exit on any error echo "=== Rust Kernel Build and Test Script ===" echo "Starting comprehensive build and validation..." +# Enable unstable features on stable compiler +export RUSTC_BOOTSTRAP=1 + # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' @@ -84,7 +87,7 @@ fi # Run Clippy lints (if available) print_status "Running Clippy lints..." if command -v cargo-clippy &> /dev/null; then - if RUSTFLAGS="-Awarnings" cargo clippy -- -D warnings > /tmp/clippy.log 2>&1; then + if cargo clippy -- -D warnings > /tmp/clippy.log 2>&1; then print_success "Clippy lints passed" else print_warning "Clippy found issues (continuing with build)" @@ -97,18 +100,18 @@ fi # Build in debug mode print_status "Building kernel in debug mode..." -run_with_status "RUSTFLAGS='-Awarnings' cargo check" "Debug build check" +run_with_status "cargo check" "Debug build check" print_success "Debug build completed successfully" # Build in release mode print_status "Building kernel in release mode..." -run_with_status "RUSTFLAGS='-Awarnings' cargo check --release" "Release build check" +run_with_status "cargo check --release" "Release build check" print_success "Release build completed successfully" # Build with make (if Makefile exists) if [ -f "Makefile" ]; then print_status "Building with Makefile..." - run_with_status "RUSTFLAGS='-Awarnings' make kernel" "Makefile build" + run_with_status "make kernel" "Makefile build" print_success "Makefile build completed successfully" else print_warning "Makefile not found, skipping make build" @@ -125,6 +128,15 @@ if [ -f "target/release/deps/kernel-"*.rlib ]; then print_status "Kernel library size: $KERNEL_SIZE" fi +# Create ISO +print_status "Creating bootable ISO..." +cp target/x86_64-unknown-none/release/rust-kernel iso/boot/rust-kernel +if grub-mkrescue -o rust-kernel.iso iso > /dev/null 2>&1; then + print_success "ISO created: rust-kernel.iso" +else + print_warning "Failed to create ISO (grub-mkrescue not found or failed)" +fi + # Create build report BUILD_REPORT="build_report.txt" print_status "Generating build report..." @@ -198,7 +210,7 @@ print_status "Test suites available: 15+ test categories" echo "" echo "To test the kernel:" -echo " 1. Boot in QEMU: qemu-system-x86_64 -kernel target/release/..." +echo " 1. Boot in QEMU: qemu-system-x86_64 -cdrom rust-kernel.iso" echo " 2. Use shell commands like: 'test run', 'sysinfo', 'health'" echo " 3. Monitor system status with: 'diag', 'perf', 'mem'" diff --git a/clippy.toml b/clippy.toml index ca4ea65..12135de 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,21 +1,21 @@ # Clippy configuration for kernel development -allow = [ - "clippy::missing_errors_doc", - "clippy::missing_panics_doc", - "clippy::module_name_repetitions", - "clippy::inline_always", - "clippy::must_use_candidate", - "clippy::cast_possible_truncation", - "clippy::cast_sign_loss", - "clippy::cast_possible_wrap", - "clippy::similar_names", - "clippy::too_many_lines", - "clippy::unused_self" -] +# allow = [ +# "clippy::missing_errors_doc", +# "clippy::missing_panics_doc", +# "clippy::module_name_repetitions", +# "clippy::inline_always", +# "clippy::must_use_candidate", +# "clippy::cast_possible_truncation", +# "clippy::cast_sign_loss", +# "clippy::cast_possible_wrap", +# "clippy::similar_names", +# "clippy::too_many_lines", +# "clippy::unused_self" +# ] # Kernel-specific thresholds cognitive-complexity-threshold = 30 too-many-arguments-threshold = 8 type-complexity-threshold = 250 -single-char-lifetime-names-threshold = 4 +# single-char-lifetime-names-threshold = 4 diff --git a/iso/boot/grub/grub.cfg b/iso/boot/grub/grub.cfg index e325b07..db9ac7b 100644 --- a/iso/boot/grub/grub.cfg +++ b/iso/boot/grub/grub.cfg @@ -2,6 +2,6 @@ set timeout=0 set default=0 menuentry "Rust Kernel" { - multiboot /boot/rust-kernel + multiboot2 /boot/rust-kernel boot } diff --git a/iso/boot/rust-kernel b/iso/boot/rust-kernel deleted file mode 100755 index 13573e8..0000000 Binary files a/iso/boot/rust-kernel and /dev/null differ diff --git a/kernel/.cargo/config.toml b/kernel/.cargo/config.toml index d060047..d5f1ca9 100644 --- a/kernel/.cargo/config.toml +++ b/kernel/.cargo/config.toml @@ -4,10 +4,9 @@ rustflags = [ "-C", "link-arg=-static", "-C", "relocation-model=static", "-C", "link-arg=-no-pie", - "-C", "link-arg=-fno-pie", "-C", "link-arg=-z", "-C", "link-arg=max-page-size=0x1000", - "-C", "link-arg=-Wl,--oformat=elf64-x86-64", + "-C", "link-arg=--oformat=elf64-x86-64", ] [build] diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index b158f18..0ebaa35 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -10,10 +10,6 @@ license = "GPL-2.0" name = "kernel" crate-type = ["rlib"] -[[bin]] -name = "rust-kernel" -path = "src/main.rs" - [dependencies] spin = "0.9" bitflags = "2.4" diff --git a/kernel/build.rs b/kernel/build.rs index af77bd9..d2ac96b 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -6,13 +6,12 @@ use std::path::PathBuf; fn main() { println!("cargo:rerun-if-changed=linker.ld"); - + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); // Copy linker script to OUT_DIR so the linker can find it let linker_script = out_dir.join("linker.ld"); - fs::copy("linker.ld", &linker_script) - .expect("Failed to copy linker script"); + fs::copy("linker.ld", &linker_script).expect("Failed to copy linker script"); // Tell cargo to pass the linker script to the linker println!("cargo:rustc-link-arg=-T{}", linker_script.display()); diff --git a/kernel/linker.ld b/kernel/linker.ld index 7266318..3cf5568 100644 --- a/kernel/linker.ld +++ b/kernel/linker.ld @@ -40,6 +40,7 @@ SECTIONS /* Read-write data */ .data : ALIGN(4K) { *(.data .data.*) + *(.got .got.*) } :data /* BSS section (uninitialized data) */ diff --git a/kernel/src/arch/x86_64/boot.s b/kernel/src/arch/x86_64/boot.s index 6bcf58b..e626552 100644 --- a/kernel/src/arch/x86_64/boot.s +++ b/kernel/src/arch/x86_64/boot.s @@ -1,7 +1,15 @@ # SPDX-License-Identifier: GPL-2.0 # Rust Kernel boot entry point for x86_64 -.section .multiboot_header +.section .multiboot_header, "a" +# Multiboot 1 Header +.align 4 + .long 0x1BADB002 # magic + .long 0x00000003 # flags (align + meminfo) + .long -(0x1BADB002 + 0x00000003) # checksum + +# Multiboot 2 Header +.align 8 header_start: # Multiboot2 header .long 0xe85250d6 # magic number @@ -21,6 +29,8 @@ header_end: .section .bss multiboot_magic_store: .skip 4 +multiboot_info_store: + .skip 4 # Stack for the kernel .global stack_bottom .global stack_top @@ -30,16 +40,16 @@ stack_top: # Bootstrap page tables .align 4096 -.global boot_page_directory_ptr_table -boot_page_directory_ptr_table: +.global boot_pml4 +boot_pml4: .skip 4096 -.global boot_page_directory_table -boot_page_directory_table: +.global boot_pdp +boot_pdp: .skip 4096 -.global boot_page_table -boot_page_table: +.global boot_pd +boot_pd: .skip 4096 .section .rodata @@ -49,7 +59,7 @@ gdt64: .quad (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) # code segment .set gdt64.data, . - gdt64 .quad (1<<44) | (1<<47) | (1<<41) # data segment -.set gdt64.pointer, . - gdt64 +gdt64.pointer: .word . - gdt64 - 1 # length .quad gdt64 # address @@ -61,13 +71,21 @@ _start: movl $stack_top, %esp movl %esp, %ebp - # Save multiboot information before we lose it + # Save multiboot information before we lose it or clobber EAX movl %eax, multiboot_magic_store movl %ebx, multiboot_info_store + # Restore magic for check (or use stored value) + movl multiboot_magic_store, %eax + # Check for multiboot cmpl $0x36d76289, %eax - jne no_multiboot + je .multiboot_ok + cmpl $0x2BADB002, %eax + je .multiboot_ok + jmp no_multiboot + +.multiboot_ok: # Check for CPUID call check_cpuid @@ -88,7 +106,7 @@ _start: movl %eax, %cr4 # Load page table - movl $boot_page_directory_ptr_table, %eax + movl $boot_pml4, %eax movl %eax, %cr3 # Enable long mode @@ -121,7 +139,7 @@ check_cpuid: pushl %ecx popfl cmpl %ecx, %eax - sete %al + setne %al movzbl %al, %eax ret @@ -136,7 +154,7 @@ check_long_mode: movl $0x80000001, %eax cpuid testl $1 << 29, %edx - setz %al + setnz %al movzbl %al, %eax ret @@ -145,30 +163,28 @@ check_long_mode: ret setup_page_tables: - # Map first 2MB with 2MB pages - # PDP table entry - movl $boot_page_directory_table, %eax - orl $0b11, %eax # present + writable - movl %eax, boot_page_directory_ptr_table + # Map PML4[0] -> PDP + movl $boot_pdp, %eax + orl $0b11, %eax # present + writable + movl %eax, boot_pml4 - # PD table entry - movl $boot_page_table, %eax - orl $0b11, %eax # present + writable - movl %eax, boot_page_directory_table + # Map PDP[0] -> PD + movl $boot_pd, %eax + orl $0b11, %eax # present + writable + movl %eax, boot_pdp - # Page table entries (identity map first 2MB) - movl $boot_page_table, %edi - movl $0, %ebx - movl $512, %ecx + # Map PD[0..511] -> 2MB Pages (Identity map 0-1GB) + movl $boot_pd, %edi + movl $0, %ebx # Physical address + movl $512, %ecx # 512 entries -.map_page_table: +.map_pd_loop: movl %ebx, %eax - shll $12, %eax # multiply by 4096 (page size) - orl $0b11, %eax # present + writable + orl $0b10000011, %eax # present + writable + huge (2MB) movl %eax, (%edi) - addl $8, %edi - incl %ebx - loop .map_page_table + addl $8, %edi # Next entry + addl $0x200000, %ebx # Next 2MB + loop .map_pd_loop ret @@ -228,6 +244,10 @@ print_string: ret no_multiboot: + # DEBUG: Print 'M' to serial port COM1 + mov $0x3f8, %dx + mov $'M', %al + out %al, %dx movl $no_multiboot_msg, %esi call print_string_32 jmp halt32 @@ -272,3 +292,5 @@ no_cpuid_msg: no_long_mode_msg: .asciz "ERROR: Long mode not supported" + + diff --git a/kernel/src/arch/x86_64/boot_entry.s b/kernel/src/arch/x86_64/boot_entry.s deleted file mode 100644 index 2e625dd..0000000 --- a/kernel/src/arch/x86_64/boot_entry.s +++ /dev/null @@ -1,44 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Kernel entry point - multiboot compliant - -.section .multiboot -.align 4 - -# Multiboot header -multiboot_header: - .long 0x1BADB002 # Magic number - .long 0x00000003 # Flags (align modules on page boundaries + memory info) - .long -(0x1BADB002 + 0x00000003) # Checksum - -.section .bss -.align 16 -stack_bottom: - .skip 16384 # 16 KB stack -stack_top: - -.section .text -.global _start -.type _start, @function - -_start: - # Set up the stack - mov $stack_top, %esp - - # Reset EFLAGS - pushl $0 - popf - - # Push multiboot parameters - pushl %ebx # Multiboot info structure - pushl %eax # Multiboot magic number - - # Call the kernel main function - call kernel_main_multiboot - - # If kernel returns (shouldn't happen), hang - cli -hang: - hlt - jmp hang - -.size _start, . - _start diff --git a/kernel/src/arch/x86_64/context.rs b/kernel/src/arch/x86_64/context.rs index 826c1de..e598579 100644 --- a/kernel/src/arch/x86_64/context.rs +++ b/kernel/src/arch/x86_64/context.rs @@ -166,32 +166,88 @@ impl Context { /// Restore CPU context and switch to it pub unsafe fn restore(&self) -> ! { - // For now, implement a simplified version that doesn't cause register pressure - // TODO: Implement full context switching with proper register restoration - - // Restore page table - asm!("mov cr3, {}", in(reg) self.cr3); - - // Set up a minimal context switch by jumping to the target RIP - // This is a simplified version - a full implementation would restore all - // registers + // Restore context using the pointer to self (passed in rdi) asm!( - "mov rsp, {}", - "push {}", // CS for iretq - "push {}", // RIP for iretq - "pushfq", // Push current flags - "pop rax", - "or rax, 0x200", // Enable interrupts - "push rax", // RFLAGS for iretq - "push {}", // CS again - "push {}", // RIP again - "iretq", - in(reg) self.rsp, - in(reg) self.cs as u64, - in(reg) self.rip, - in(reg) self.cs as u64, - in(reg) self.rip, - options(noreturn) + // Restore CR3 (Page Table) + "mov rax, [rdi + 144]", + "mov cr3, rax", + + // Switch stack to the target stack + "mov rsp, [rdi + 56]", + + // Construct interrupt stack frame for iretq + // Stack layout: SS, RSP, RFLAGS, CS, RIP + + // SS + "movzx rax, word ptr [rdi + 162]", + "push rax", + + // RSP (target stack pointer) + "mov rax, [rdi + 56]", + "push rax", + + // RFLAGS + "mov rax, [rdi + 136]", + "push rax", + + // CS + "movzx rax, word ptr [rdi + 152]", + "push rax", + + // RIP + "mov rax, [rdi + 128]", + "push rax", + + // Push General Purpose Registers onto the new stack + // We push them in reverse order of popping + "push qword ptr [rdi + 0]", // rax + "push qword ptr [rdi + 8]", // rbx + "push qword ptr [rdi + 16]", // rcx + "push qword ptr [rdi + 24]", // rdx + "push qword ptr [rdi + 32]", // rsi + "push qword ptr [rdi + 40]", // rdi + "push qword ptr [rdi + 48]", // rbp + // rsp is handled by stack switch + "push qword ptr [rdi + 64]", // r8 + "push qword ptr [rdi + 72]", // r9 + "push qword ptr [rdi + 80]", // r10 + "push qword ptr [rdi + 88]", // r11 + "push qword ptr [rdi + 96]", // r12 + "push qword ptr [rdi + 104]", // r13 + "push qword ptr [rdi + 112]", // r14 + "push qword ptr [rdi + 120]", // r15 + + // Restore Segment Registers + "mov ax, [rdi + 154]", // ds + "mov ds, ax", + "mov ax, [rdi + 156]", // es + "mov es, ax", + "mov ax, [rdi + 158]", // fs + "mov fs, ax", + "mov ax, [rdi + 160]", // gs + "mov gs, ax", + + // Pop General Purpose Registers + "pop r15", + "pop r14", + "pop r13", + "pop r12", + "pop r11", + "pop r10", + "pop r9", + "pop r8", + "pop rbp", + "pop rdi", // This restores the target rdi + "pop rsi", + "pop rdx", + "pop rcx", + "pop rbx", + "pop rax", + + // Return from interrupt (restores RIP, CS, RFLAGS, RSP, SS) + "iretq", + in("rdi") self, + options(noreturn) ); } } diff --git a/kernel/src/boot.rs b/kernel/src/boot.rs index a761851..efc7917 100644 --- a/kernel/src/boot.rs +++ b/kernel/src/boot.rs @@ -74,7 +74,9 @@ pub fn get_boot_info() -> &'static BootInfo { /// Update boot information pub unsafe fn update_boot_info(f: F) -where F: FnOnce(&mut BootInfo) { +where + F: FnOnce(&mut BootInfo), +{ f(&mut BOOT_INFO); } @@ -114,7 +116,8 @@ pub mod multiboot { pub struct BootMemoryInfo { pub total_memory: u64, pub available_memory: u64, - pub memory_regions: alloc::vec::Vec, + pub memory_regions: [MemoryMapEntry; 32], + pub region_count: usize, } impl BootMemoryInfo { @@ -122,7 +125,13 @@ pub mod multiboot { Self { total_memory: 0, available_memory: 0, - memory_regions: alloc::vec::Vec::new(), + memory_regions: [MemoryMapEntry { + base_addr: 0, + length: 0, + type_: 0, + reserved: 0, + }; 32], + region_count: 0, } } @@ -131,17 +140,20 @@ pub mod multiboot { self.available_memory += entry.length; } self.total_memory += entry.length; - self.memory_regions.push(entry); + if self.region_count < 32 { + self.memory_regions[self.region_count] = entry; + self.region_count += 1; + } } } /// Parse multiboot2 information and initialize memory management pub fn init_memory_from_multiboot(multiboot_addr: usize) -> Result<()> { - info!("Parsing multiboot information at 0x{:x}", multiboot_addr); + crate::println!("Parsing multiboot information at 0x{:x}", multiboot_addr); let multiboot_info = unsafe { &*(multiboot_addr as *const MultibootInfo) }; - info!("Multiboot info size: {} bytes", multiboot_info.total_size); + crate::println!("Multiboot info size: {} bytes", multiboot_info.total_size); // Parse memory map from multiboot info let mut memory_info = BootMemoryInfo::new(); @@ -166,12 +178,13 @@ pub mod multiboot { } // Initialize page allocator with available memory - for region in &memory_info.memory_regions { + for i in 0..memory_info.region_count { + let region = &memory_info.memory_regions[i]; if region.type_ == memory_type::AVAILABLE { let start_pfn = region.base_addr / 4096; let end_pfn = (region.base_addr + region.length) / 4096; - info!( + crate::println!( "Adding memory region: 0x{:x}-0x{:x}", region.base_addr, region.base_addr + region.length @@ -185,9 +198,9 @@ pub mod multiboot { } } - info!("Memory initialization from multiboot completed"); - info!("Total memory: {} bytes", memory_info.total_memory); - info!("Available memory: {} bytes", memory_info.available_memory); + crate::println!("Memory initialization from multiboot completed"); + crate::println!("Total memory: {} bytes", memory_info.total_memory); + crate::println!("Available memory: {} bytes", memory_info.available_memory); Ok(()) } diff --git a/kernel/src/boot.s b/kernel/src/boot.s deleted file mode 100644 index b3e48d0..0000000 --- a/kernel/src/boot.s +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Multiboot header and boot code - -.code32 - -.section .multiboot_header, "a" -.align 4 - -# Multiboot header -multiboot_header_start: - .long 0x1BADB002 # magic number (multiboot 1) - .long 0x00000000 # flags - .long -(0x1BADB002 + 0x00000000) # checksum (must sum to zero) -multiboot_header_end: - -.section .text -.global _start -.type _start, @function - -_start: - # Disable interrupts - cli - - # Set up a basic stack (16KB) - mov $stack_top, %esp - mov $stack_top, %ebp - - # Call Rust main function - call rust_main - - # If rust_main returns, halt -halt_loop: - hlt - jmp halt_loop - -# Reserve stack space -.section .bss -.align 16 -stack_bottom: - .skip 16384 # 16KB stack -stack_top: diff --git a/kernel/src/enhanced_scheduler.rs b/kernel/src/enhanced_scheduler.rs index abf9bc5..83a21ee 100644 --- a/kernel/src/enhanced_scheduler.rs +++ b/kernel/src/enhanced_scheduler.rs @@ -462,7 +462,9 @@ static ENHANCED_SCHEDULER: Spinlock> = Spinlock::new(N /// Helper to get scheduler reference safely fn with_scheduler(f: F) -> Option -where F: FnOnce(&mut EnhancedScheduler) -> T { +where + F: FnOnce(&mut EnhancedScheduler) -> T, +{ let mut scheduler_option = ENHANCED_SCHEDULER.lock(); if let Some(ref mut scheduler) = *scheduler_option { Some(f(scheduler)) @@ -473,7 +475,9 @@ where F: FnOnce(&mut EnhancedScheduler) -> T { /// Helper to get read-only scheduler reference safely fn with_scheduler_read(f: F) -> Option -where F: FnOnce(&EnhancedScheduler) -> T { +where + F: FnOnce(&EnhancedScheduler) -> T, +{ let scheduler_option = ENHANCED_SCHEDULER.lock(); if let Some(ref scheduler) = *scheduler_option { Some(f(scheduler)) diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index f3013fd..d72b399 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -18,6 +18,10 @@ extern crate alloc; +// Include boot assembly +// #[cfg(target_arch = "x86_64")] +// global_asm!(include_str!("arch/x86_64/boot.s"), options(att_syntax)); + pub mod arch; pub mod benchmark; // Performance benchmarking pub mod boot; @@ -94,7 +98,7 @@ pub extern "C" fn kernel_main() -> ! { #[no_mangle] pub extern "C" fn kernel_main_multiboot(multiboot_magic: u32, multiboot_addr: u32) -> ! { // Verify multiboot magic number - if multiboot_magic != 0x36d76289 { + if multiboot_magic != 0x36d76289 && multiboot_magic != 0x2BADB002 { panic!("Invalid multiboot magic: 0x{:x}", multiboot_magic); } @@ -113,8 +117,8 @@ fn early_kernel_init() { loop {} } - info!("Rust Kernel v{} starting...", VERSION); - info!("Early kernel initialization"); + crate::println!("Rust Kernel v{} starting...", VERSION); + crate::println!("Early kernel initialization"); } /// Initialize memory management using multiboot information diff --git a/kernel/src/main.rs b/kernel/src/main.rs index d00fb30..c99913b 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -5,83 +5,51 @@ #![no_std] #![no_main] +extern crate kernel; + +use core::arch::global_asm; + +// Include boot assembly +#[cfg(target_arch = "x86_64")] +global_asm!(include_str!("arch/x86_64/boot.s"), options(att_syntax)); + +use core::panic::PanicInfo; + /// Multiboot1 header - placed at the very beginning #[repr(C)] #[repr(packed)] struct MultibootHeader { - magic: u32, - flags: u32, - checksum: u32, + magic: u32, + flags: u32, + checksum: u32, } /// Multiboot header must be in the first 8KB and be 4-byte aligned -#[link_section = ".multiboot_header"] -#[no_mangle] -#[used] -static MULTIBOOT_HEADER: MultibootHeader = MultibootHeader { - magic: 0x1BADB002, - flags: 0x00000000, - checksum: 0u32.wrapping_sub(0x1BADB002u32.wrapping_add(0x00000000)), -}; +// #[link_section = ".multiboot_header"] +// #[no_mangle] +// #[used] +// static MULTIBOOT_HEADER: MultibootHeader = MultibootHeader { +// magic: 0x1BADB002, +// flags: 0x00000000, +// checksum: 0u32.wrapping_sub(0x1BADB002u32.wrapping_add(0x00000000)), +// }; /// Entry point called by boot.s assembly code +/// This is just a wrapper to ensure the kernel crate is linked #[no_mangle] pub extern "C" fn rust_main() -> ! { - kernel_main() + // This function shouldn't be called directly if boot.s calls kernel_main_multiboot + // But if it is called, we redirect to the kernel library + loop { + unsafe { + core::arch::asm!("hlt"); + } + } } -/// Main kernel function -fn kernel_main() -> ! { - // Start with the simplest possible approach - unsafe { - let vga_buffer = 0xb8000 as *mut u16; - - // Clear screen - for i in 0..80*25 { - *vga_buffer.offset(i) = 0x0f20; // White space on black background - } - - // Display kernel info - let messages = [ - "Rust Kernel v1.0 - Successfully Booted!", - "", - "Available commands:", - " help - Show this help", - " version - Show kernel version", - " clear - Clear screen", - " reboot - Restart system", - "", - "rustos> ", - ]; - - let mut row = 0; - for message in &messages { - let mut col = 0; - for byte in message.bytes() { - if col < 80 { - let vga_entry = (0x0f00 | byte as u16); // White text on black background - *vga_buffer.offset((row * 80 + col) as isize) = vga_entry; - col += 1; - } - } - row += 1; - } - } - - // Simple command loop (just display, no real interaction yet) - loop { - unsafe { - core::arch::asm!("hlt"); - } - } -} +// Panic handler is defined in the kernel library -/// Panic handler - required for no_std -#[panic_handler] -fn panic(_info: &core::panic::PanicInfo) -> ! { - loop { - unsafe { - core::arch::asm!("hlt"); - } - } +#[no_mangle] +pub extern "C" fn _start() -> ! { + loop {} } diff --git a/kernel/src/memory/page.rs b/kernel/src/memory/page.rs index ee1b0de..a2288d3 100644 --- a/kernel/src/memory/page.rs +++ b/kernel/src/memory/page.rs @@ -2,7 +2,6 @@ //! Page frame allocator -use alloc::collections::BTreeSet; use core::sync::atomic::{AtomicU32, Ordering}; use crate::error::{Error, Result}; @@ -98,34 +97,57 @@ pub static PAGE_ALLOCATOR: Spinlock = Spinlock::new(PageAllocator /// Page allocator implementation pub struct PageAllocator { - free_pages: BTreeSet, + free_list_head: Option, total_pages: usize, allocated_pages: usize, + free_count: usize, } impl PageAllocator { pub const fn new() -> Self { Self { - free_pages: BTreeSet::new(), + free_list_head: None, total_pages: 0, allocated_pages: 0, + free_count: 0, } } /// Add a range of pages to the free list pub fn add_free_range(&mut self, start: Pfn, count: usize) { for i in 0..count { - self.free_pages.insert(Pfn(start.0 + i)); + let pfn = Pfn(start.0 + i); + let phys_addr = PhysAddr(pfn.0 * 4096); + + // Store current head in the new page + // We can write to phys_addr because it's identity mapped + unsafe { + let ptr = phys_addr.0 as *mut u64; + *ptr = self.free_list_head.map(|a| a.0 as u64).unwrap_or(0); + } + + // Update head + self.free_list_head = Some(phys_addr); } self.total_pages += count; + self.free_count += count; } /// Allocate a single page fn alloc_page(&mut self) -> Result { - if let Some(pfn) = self.free_pages.iter().next().copied() { - self.free_pages.remove(&pfn); + if let Some(head_addr) = self.free_list_head { + // Read next ptr from head + let next_addr_u64 = unsafe { *(head_addr.0 as *const u64) }; + + self.free_list_head = if next_addr_u64 == 0 { + None + } else { + Some(PhysAddr(next_addr_u64 as usize)) + }; + self.allocated_pages += 1; - Ok(pfn) + self.free_count -= 1; + Ok(Pfn(head_addr.0 / 4096)) } else { Err(Error::OutOfMemory) } @@ -133,18 +155,19 @@ impl PageAllocator { /// Free a single page fn free_page(&mut self, pfn: Pfn) { - if self.free_pages.insert(pfn) { - self.allocated_pages -= 1; + let phys_addr = PhysAddr(pfn.0 * 4096); + unsafe { + let ptr = phys_addr.0 as *mut u64; + *ptr = self.free_list_head.map(|a| a.0 as u64).unwrap_or(0); } + self.free_list_head = Some(phys_addr); + self.allocated_pages -= 1; + self.free_count += 1; } /// Get statistics fn stats(&self) -> (usize, usize, usize) { - ( - self.total_pages, - self.allocated_pages, - self.free_pages.len(), - ) + (self.total_pages, self.allocated_pages, self.free_count) } } diff --git a/kernel/src/multiboot.s b/kernel/src/multiboot.s deleted file mode 100644 index 819e7b4..0000000 --- a/kernel/src/multiboot.s +++ /dev/null @@ -1,16 +0,0 @@ -.section .multiboot_header -.align 4 - -.long 0x1BADB002 /* magic */ -.long 0x00000000 /* flags */ -.long -(0x1BADB002 + 0x00000000) /* checksum */ - -.section .text -.global _start -_start: - /* Call Rust main function */ - call rust_main - - /* If rust_main returns, halt */ -1: hlt - jmp 1b diff --git a/kernel/src/process.rs b/kernel/src/process.rs index 93f415e..35c284e 100644 --- a/kernel/src/process.rs +++ b/kernel/src/process.rs @@ -251,6 +251,29 @@ impl ProcessTable { } None } + + pub fn find_two_threads_mut( + &mut self, + tid1: Tid, + tid2: Tid, + ) -> (Option<&mut Thread>, Option<&mut Thread>) { + if tid1 == tid2 { + let t = self.find_thread_mut(tid1); + return (t, None); + } + + // This is a bit inefficient but safe + // We can't easily return two mutable references to the same structure + // But since they are in different processes or different threads, they are distinct memory locations. + // We can use unsafe to cheat the borrow checker, knowing that tid1 != tid2. + + let ptr = self as *mut ProcessTable; + unsafe { + let t1 = (*ptr).find_thread_mut(tid1); + let t2 = (*ptr).find_thread_mut(tid2); + (t1, t2) + } + } } /// Allocate a new PID @@ -279,6 +302,20 @@ pub fn create_process(name: String, uid: Uid, gid: Gid) -> Result { Ok(pid) } +/// Add a thread to the kernel process (PID 0) +pub fn add_kernel_thread(tid: Tid, context: Context, stack_pointer: VirtAddr) -> Result<()> { + let mut table = PROCESS_TABLE.lock(); + if let Some(process) = table.get_process_mut(Pid(0)) { + let mut thread = Thread::new(tid, Pid(0), 0); + thread.context = context; + thread.stack_pointer = stack_pointer; + process.add_thread(thread); + Ok(()) + } else { + Err(Error::NotFound) + } +} + /// Get current process PID pub fn current_process_pid() -> Option { let table = PROCESS_TABLE.lock(); diff --git a/kernel/src/scheduler.rs b/kernel/src/scheduler.rs index 39ca736..a15de09 100644 --- a/kernel/src/scheduler.rs +++ b/kernel/src/scheduler.rs @@ -458,32 +458,39 @@ impl Scheduler { if let Some(current_tid) = self.current { if current_tid != tid { // Look up current and next threads - let process_table = PROCESS_TABLE.lock(); - if let (Some(current_thread), Some(next_thread)) = ( - process_table.find_thread(current_tid), - process_table.find_thread(tid), - ) { - // Update scheduler state - self.current = Some(tid); - self.nr_switches += 1; + // We need to use a scope to ensure the lock is dropped before switching + let (current_ctx_ptr, next_ctx_ptr) = { + let mut process_table = PROCESS_TABLE.lock(); - // Drop the lock before context switch to avoid deadlock - drop(process_table); + let (current_thread, next_thread) = process_table + .find_two_threads_mut(current_tid, tid); - // TODO: Implement actual context switch - // This would involve: - // 1. Saving current thread's context - // 2. Loading next thread's context - // 3. Switching page tables if different processes - // 4. Updating stack pointer and instruction pointer + let current_ptr = if let Some(t) = current_thread { + &mut t.context as *mut Context + } else { + return; // Current thread not found? + }; - crate::info!( - "Context switch from TID {} to TID {}", - current_tid.0, - tid.0 - ); - return; + let next_ptr = if let Some(t) = next_thread { + &t.context as *const Context + } else { + return; // Next thread not found + }; + + (current_ptr, next_ptr) + }; + + // Update scheduler state + self.current = Some(tid); + self.nr_switches += 1; + + // Perform the context switch + // SAFETY: We have valid pointers to the contexts and we've dropped the lock + unsafe { + switch_context(&mut *current_ctx_ptr, &*next_ctx_ptr); } + + return; } } @@ -654,3 +661,10 @@ pub fn scheduler_tick() { } } } + +/// Perform a manual context switch to a specific task +/// This is used by the enhanced scheduler to execute its scheduling decisions +pub fn context_switch_to(tid: Tid) { + let mut scheduler = SCHEDULER.lock(); + scheduler.switch_to(tid); +} diff --git a/kernel/src/time.rs b/kernel/src/time.rs index 685d083..151dd21 100644 --- a/kernel/src/time.rs +++ b/kernel/src/time.rs @@ -403,8 +403,37 @@ impl TimerWheel { } pub fn add_timer(&mut self, timer: HrTimer) { - // TODO: Add timer to appropriate level based on expiry time - let level = 0; // Simplified + let now_ns = get_time_ns(); + let expires_ns = timer.expires.to_ns(); + + // If already expired or expires very soon, put in level 0 + if expires_ns <= now_ns { + self.levels[0].push(timer); + return; + } + + let delta_ns = expires_ns - now_ns; + let delta_jiffies = delta_ns / NSEC_PER_JIFFY; + + // Determine level based on delta + // Level 0: Immediate to ~256ms + // Level 1: ~256ms to ~16s + // Level 2: ~16s to ~17m + // Level 3: ~17m to ~18h + // Level 4+: Far future + let level = if delta_jiffies < 256 { + 0 + } else if delta_jiffies < 256 * 64 { + 1 + } else if delta_jiffies < 256 * 64 * 64 { + 2 + } else if delta_jiffies < 256 * 64 * 64 * 64 { + 3 + } else { + 4 + }; + + let level = core::cmp::min(level, 7); self.levels[level].push(timer); } diff --git a/kernel/src/timer.rs b/kernel/src/timer.rs index 8e055b9..6f7e066 100644 --- a/kernel/src/timer.rs +++ b/kernel/src/timer.rs @@ -93,9 +93,10 @@ impl TimerState { let mut stats = self.stats.lock(); stats.context_switches += 1; - // TODO: Actual context switching would happen here - // This would involve saving current CPU state and - // restoring the state of the next task + // Perform actual context switch + // This will save current CPU state and restore the state of the next task + crate::scheduler::context_switch_to(next_tid); + crate::info!( "Context switch: {:?} -> {:?}", current_tid, diff --git a/kernel/src/working_task.rs b/kernel/src/working_task.rs index bc231f1..4d72da2 100644 --- a/kernel/src/working_task.rs +++ b/kernel/src/working_task.rs @@ -50,8 +50,8 @@ impl Task { function: TaskFunction, stack_size: usize, ) -> Result { - static NEXT_TID: AtomicU32 = AtomicU32::new(1); - let tid = Tid(NEXT_TID.fetch_add(1, Ordering::Relaxed)); + // Use process subsystem to allocate TID to ensure uniqueness + let tid = crate::process::allocate_tid(); // Allocate stack let stack_ptr = kmalloc::kmalloc(stack_size)?; @@ -156,10 +156,18 @@ impl TaskManager { function: TaskFunction, stack_size: usize, ) -> Result { - let task = Task::new_kernel_task(name, function, stack_size)?; + let task = Task::new_kernel_task(name.clone(), function, stack_size)?; let tid = task.tid; - self.tasks.lock().push(task); + // Add to local task list + self.tasks.lock().push(task.clone()); + + // Add to PROCESS_TABLE so the low-level scheduler can find it + crate::process::add_kernel_thread( + tid, + task.context, + crate::memory::VirtAddr::new(task.context.rsp as usize), + )?; // Add to enhanced scheduler crate::enhanced_scheduler::add_task( diff --git a/rust-kernel.iso b/rust-kernel.iso deleted file mode 100644 index 6ba27dd..0000000 Binary files a/rust-kernel.iso and /dev/null differ diff --git a/src/lib.rs b/src/lib.rs index 2b372c8..50d6ebb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,8 +14,8 @@ pub use kernel::*; #[cfg(test)] mod tests { - #[test] - fn basic_test() { - assert_eq!(2 + 2, 4); - } + #[test] + fn basic_test() { + assert_eq!(2 + 2, 4); + } } diff --git a/src/main.rs b/src/main.rs index 5ca72c7..7f52340 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,8 +9,11 @@ extern crate kernel; -/// Main kernel entry point -#[no_mangle] -pub extern "C" fn _start() -> ! { - kernel::kernel_main() -} +use core::arch::global_asm; + +// Include boot assembly +#[cfg(target_arch = "x86_64")] +global_asm!( + include_str!("../kernel/src/arch/x86_64/boot.s"), + options(att_syntax) +);