From f943cf5426c822ce17af560a1a516f849e745042 Mon Sep 17 00:00:00 2001 From: csd4ni3l <96988024+csd4ni3l@users.noreply.github.com> Date: Sat, 30 May 2026 19:10:26 +0200 Subject: [PATCH] Increase kernel heap size to fit more ELFs, improve scheduler by removing O(n) operations (ready and sleep queues now), caching timer ticks per irq on init_interrupts aarch64, only saving context when switching, add kill syscall and use it for exit, only push input to init, fix accidental REL_X events drop with virtio, fix the possibility of not having a process to switch to by either staying, switching or idling, improve scheduler by removing double locking and splitting code to multiple functions, do not switch every IRQ but only 100 IRQs, improve rust app building, move x86_64 kernel fb so it doesn't hit heap, update submodules --- build_rust_app.sh | 30 ++- config.mk | 3 +- kernel/src/arch/aarch64/heap.rs | 2 +- kernel/src/arch/aarch64/interrupts.rs | 25 +- kernel/src/arch/syscall.rs | 70 +++--- kernel/src/arch/x86_64/heap.rs | 2 +- kernel/src/arch/x86_64/interrupts.rs | 28 ++- kernel/src/arch/x86_64/syscall.rs | 25 ++ kernel/src/config.rs | 2 +- kernel/src/driver/elf/loader.rs | 2 + kernel/src/driver/framebuffer.rs | 2 +- kernel/src/driver/io/fs/assets.rs | 10 +- kernel/src/driver/io/input.rs | 2 +- kernel/src/driver/io/virtio/input.rs | 4 - kernel/src/main.rs | 1 + kernel/src/mm/shm.rs | 2 +- kernel/src/task/process.rs | 4 +- kernel/src/task/scheduler.rs | 330 ++++++++++++++++++-------- user/apps/doomgeneric | 2 +- user/apps/shell | 2 +- user/init | 2 +- user/libxunil | 2 +- 22 files changed, 371 insertions(+), 181 deletions(-) diff --git a/build_rust_app.sh b/build_rust_app.sh index e91e37c..ab38d46 100644 --- a/build_rust_app.sh +++ b/build_rust_app.sh @@ -1,13 +1,25 @@ #!/bin/bash -if [[ "$1" = "init" || "$1" = "libxunil" ]]; then - cd user/$1 + +if [[ "$1" == "init" || "$1" == "libxunil" ]]; then + base="user/$1" else - cd user/apps/$1 + base="user/apps/$1" fi -cargo build --target $KARCH-unknown-none --release --config profile.release.debug=true -if [[ "$1" = "init" || "$1" = "libxunil" ]]; then - cp ./target/$KARCH-unknown-none/release/$1 ../../assets/$KARCH/$1 -else - cp ./target/$KARCH-unknown-none/release/$1 ../../../assets/$KARCH/$1 -fi +cd "$base" || exit 1 + +cargo build --target "$KARCH-unknown-none" --release \ + --config profile.release.debug=true + +case "$1" in + init) + cp "./target/$KARCH-unknown-none/release/$1" \ + "../../assets/$KARCH/$1" + ;; + libxunil) + ;; + *) + cp "./target/$KARCH-unknown-none/release/$1" \ + "../../../assets/$KARCH/$1" + ;; +esac diff --git a/config.mk b/config.mk index 11f68b9..3da1191 100644 --- a/config.mk +++ b/config.mk @@ -1,4 +1,3 @@ -export KARCH ?= x86_64 export OUTPUT ?= kernel -export MEMORY ?= 1G +export MEMORY ?= 4G export TIMER_FREQUENCY_HZ ?= 1000 diff --git a/kernel/src/arch/aarch64/heap.rs b/kernel/src/arch/aarch64/heap.rs index 30a5634..f742869 100644 --- a/kernel/src/arch/aarch64/heap.rs +++ b/kernel/src/arch/aarch64/heap.rs @@ -7,7 +7,7 @@ use crate::{ pub static ALLOCATOR: Locked = Locked::new(LinkedListAllocator::new()); pub const HEAP_START: usize = 0xffffffff90000000; -pub const HEAP_SIZE: usize = 64 * 1024 * 1024; // 64 MiB +pub const HEAP_SIZE: usize = 512 * 1024 * 1024; // 512 MiB pub fn init_heap(mapper: &mut AArchPageTable) { let pages = HEAP_SIZE / 4096; diff --git a/kernel/src/arch/aarch64/interrupts.rs b/kernel/src/arch/aarch64/interrupts.rs index fc05054..b6c7c6b 100644 --- a/kernel/src/arch/aarch64/interrupts.rs +++ b/kernel/src/arch/aarch64/interrupts.rs @@ -6,7 +6,8 @@ use crate::{ driver::io::virtio::{KEYBOARD_SLOT, MOUSE_SLOT, input::input_interrupt}, task::{ context::{UserContext, ctx_save}, - scheduler::check_and_reschedule, + process::ProcessState, + scheduler::{SCHEDULER, check_and_reschedule, current_pid}, }, }; @@ -39,6 +40,8 @@ global_asm!( "# ); +static mut TIMER_TICKS_PER_IRQ: u64 = 0; + fn timer_ticks_per_irq() -> u64 { let cntfrq: u64; unsafe { core::arch::asm!("mrs {}, cntfrq_el0", out(reg) cntfrq) }; @@ -161,6 +164,7 @@ unsafe fn gic_eoi(id: u32) { } pub fn init_interrupts() { + unsafe { TIMER_TICKS_PER_IRQ = timer_ticks_per_irq() }; unsafe { use_exception_vectors() }; unsafe { gic_init() }; enable_interrupts(); @@ -203,17 +207,15 @@ unsafe extern "C" fn irq_handler(ctx: *mut UserContext) { match interrupt_id { 30 => { - let ticks = timer_ticks_per_irq(); - unsafe { core::arch::asm!("msr cntp_tval_el0, {}", in(reg) ticks) }; + unsafe { core::arch::asm!("msr cntp_tval_el0, {}", in(reg) TIMER_TICKS_PER_IRQ) }; unsafe { core::arch::asm!("msr cntp_ctl_el0, {}", in(reg) 1u64) }; unsafe { gic_eoi(interrupt_id); } - ctx_save(ctx); do_interrupt(); - check_and_reschedule(); + check_and_reschedule(&ctx); } interrupt_id if keyboard_irq == interrupt_id as u64 => { input_interrupt("kbd"); @@ -276,7 +278,18 @@ unsafe extern "C" fn sync_handler_user(ctx: *mut UserContext) { ) } as u64; - ctx_save(ctx as *const UserContext); + if let Some(pid) = current_pid() { + let guard = SCHEDULER.lock(); + let needs_yield = guard + .processes + .get(&pid) + .map_or(false, |p| !matches!(p.state, ProcessState::Running)); + drop(guard); + if needs_yield { + ctx_save(ctx); + SCHEDULER.switch_next(true); + } + } } _ => handle_aborts(ec, ctx), }; diff --git a/kernel/src/arch/syscall.rs b/kernel/src/arch/syscall.rs index f154cce..0f6a4de 100644 --- a/kernel/src/arch/syscall.rs +++ b/kernel/src/arch/syscall.rs @@ -1,4 +1,5 @@ #![allow(dead_code, unused_imports)] +use core::cmp::Reverse; use core::sync::atomic::Ordering; #[cfg(target_arch = "x86_64")] @@ -85,7 +86,7 @@ type PageTable<'a> = OffsetPageTable<'a>; fn map_framebuffer() -> isize { let pid = current_pid().unwrap_or(0); - if pid == 0 { + if pid != 1 { return -1; } @@ -208,6 +209,12 @@ fn close(fd: isize) -> isize { } fn input_read(user_ptr: *mut InputEvent, max_events: isize) -> isize { + let pid = current_pid().unwrap_or(0); + + if pid != 1 { + return -1; + } + #[cfg(target_arch = "x86_64")] process_scancodes(); @@ -217,12 +224,6 @@ fn input_read(user_ptr: *mut InputEvent, max_events: isize) -> isize { return -1; } - let pid = current_pid().unwrap_or(0); - - if pid == 0 { - return -1; - } - return SCHEDULER .with_process(pid as u64, |process| { let to_copy = (max_events as usize).min(process.input_buffer.len()); @@ -378,36 +379,18 @@ pub fn exec(arg0: isize) -> isize { 0 } -pub fn exit() -> isize { - serial_print("Process Exited."); - let pid = current_pid().unwrap_or(0); - if pid == 0 { - return 0; +pub fn kill(pid: isize, exit_code: isize) -> isize { + let pid = pid as u64; + println!("PID {} Exited.", pid); + + SCHEDULER.terminate_process(pid, exit_code); + + if pid == current_pid().unwrap_or(0) { + SCHEDULER.switch_next(false); + crate::arch::arch::infinite_idle(); + } else { + 0 } - - let next_pid = { - let mut sched = SCHEDULER.lock(); - - sched.processes.remove(&pid); - - sched - .processes - .iter() - .find_map(|(other, proc)| { - if *other != pid && matches!(proc.state, ProcessState::Ready) { - Some(*other) - } else { - None - } - }) - .unwrap_or(0) - }; - - if next_pid != 0 { - SCHEDULER.switch_to(next_pid, false); - } - - crate::arch::arch::infinite_idle(); } fn sleep(ticks: isize) -> isize { @@ -419,9 +402,15 @@ fn sleep(ticks: isize) -> isize { return 0; } - SCHEDULER.with_process(pid, |process| { - process.info.wake_tick = Some(TIMER.now().elapsed() + ticks as u64); - process.state = ProcessState::Blocked; + safe_lock(|| { + let mut guard = SCHEDULER.lock(); + if let Some(process) = guard.processes.get_mut(&pid) { + let wake_at = TIMER.now().elapsed() + ticks as u64; + process.info.wake_tick = Some(wake_at); + process.state = ProcessState::Blocked; + process.in_ready_queue = false; + guard.sleep_queue.push(Reverse((wake_at, pid))); + }; }); 0 @@ -686,7 +675,7 @@ pub unsafe extern "C" fn syscall_dispatch( OPEN => open(arg0, arg1), CLOSE => close(arg0), LSEEK => vfs_lseek(arg0 as i64, arg1 as i64, arg2 as i32) as isize, - EXIT => exit(), + EXIT => kill(current_pid().unwrap() as isize, arg0), SLEEP => sleep(arg0), EXECVE => exec(arg0), CLOCK_GETTIME => ((TIMER.now().elapsed() as usize) * (TIMER_FREQUENCY_HZ / 1000)) as isize, @@ -705,6 +694,7 @@ pub unsafe extern "C" fn syscall_dispatch( IPC_WRITE => ipc_write(arg0, arg1), IPC_MANAGE => ipc_manage(arg0, arg1, arg2), SHM_OPEN => shm_open(arg0, arg1), + KILL => kill(arg0, -9), FRAMEBUFFER_SWAP => { with_framebuffer(|fb| fb.present()); 0 diff --git a/kernel/src/arch/x86_64/heap.rs b/kernel/src/arch/x86_64/heap.rs index 733daca..9205321 100644 --- a/kernel/src/arch/x86_64/heap.rs +++ b/kernel/src/arch/x86_64/heap.rs @@ -9,7 +9,7 @@ use x86_64::structures::paging::{ pub static ALLOCATOR: Locked = Locked::new(LinkedListAllocator::new()); pub const HEAP_START: usize = 0xffffffff90000000; -pub const HEAP_SIZE: usize = 64 * 1024 * 1024; // 64 MiB +pub const HEAP_SIZE: usize = 512 * 1024 * 1024; // 512 MiB pub fn init_heap(mapper: &mut OffsetPageTable) -> Result<(), MapToError> { let page_count = HEAP_SIZE / 4096; diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index ae50e39..9be38cd 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -1,9 +1,13 @@ use core::arch::naked_asm; use crate::{ - arch::x86_64::gdt, + arch::{arch::do_interrupt, x86_64::gdt}, driver::io::ps2::{keyboard_interrupt, mouse_interrupt, push_scancode}, println, + task::{ + context::UserContext, + scheduler::{check_and_reschedule, current_pid}, + }, }; use lazy_static::lazy_static; use pic8259::ChainedPics; @@ -78,17 +82,21 @@ pub extern "x86-interrupt" fn page_fault_handler( error_code: PageFaultErrorCode, ) { panic!( - "EXCEPTION: PAGE FAULT\nAccessed Addresss: {:?}\nError Code: {:?}\n{:#?}", + "EXCEPTION: PAGE FAULT\nAccessed Address: {:?}\nError Code: {:?}\nCurrent PID: {}\nPER_CPU: {:#x}\n{:#?}", Cr2::read(), error_code, + current_pid().unwrap_or(0), + &raw const crate::arch::x86_64::syscall::PER_CPU as u64, stack_frame ); } pub extern "x86-interrupt" fn gpf_handler(stack_frame: InterruptStackFrame, error_code: u64) { panic!( - "EXCEPTION: GENERAL PROTECTION FAULT\nError Code: {:?}\n{:#?}", - error_code, stack_frame + "EXCEPTION: GENERAL PROTECTION FAULT\nError Code: {:?}\nCurrent PID: {}\n{:#?}", + error_code, + current_pid().unwrap_or(0), + stack_frame ); } @@ -132,10 +140,7 @@ pub fn timer_interrupt_handler() { mov rdi, rsp - call ctx_save - call do_interrupt - call eoi - call check_and_reschedule + call x86_interrupt test rax, rax jnz .switched @@ -202,6 +207,13 @@ pub fn timer_interrupt_handler() { ) } +#[unsafe(no_mangle)] +extern "C" fn x86_interrupt(ctx: *mut UserContext) -> isize { + do_interrupt(); + eoi(); + check_and_reschedule(&ctx) +} + #[unsafe(no_mangle)] extern "C" fn eoi() { unsafe { diff --git a/kernel/src/arch/x86_64/syscall.rs b/kernel/src/arch/x86_64/syscall.rs index 16eb8de..feb8a32 100644 --- a/kernel/src/arch/x86_64/syscall.rs +++ b/kernel/src/arch/x86_64/syscall.rs @@ -3,8 +3,11 @@ use core::arch::asm; use x86_64::instructions::tlb::flush_all; use crate::{ + arch::arch::safe_lock, arch::x86_64::gdt::{GDT, TSS_MUTEX}, task::context::UserContext, + task::process::ProcessState, + task::scheduler::{SCHEDULER, current_pid}, }; const IA32_EFER: u32 = 0xC0000080; @@ -129,6 +132,8 @@ unsafe extern "C" fn syscall_entry() { call ctx_save mov rsp, rbx + sti + mov rdi, qword ptr [rbx + 112] mov rsi, qword ptr [rbx + 72] mov rdx, qword ptr [rbx + 64] @@ -149,6 +154,9 @@ unsafe extern "C" fn syscall_entry() { call ctx_save mov rsp, rbx + mov rdi, rbx + call syscall_yield_check + mov rax, qword ptr [rsp + 128] mov qword ptr [rsp + 96], rax mov rax, qword ptr [rsp + 136] @@ -170,6 +178,7 @@ unsafe extern "C" fn syscall_entry() { mov r13, qword ptr [rsp + 16] mov r14, qword ptr [rsp + 8] mov r15, qword ptr [rsp + 0] + cli mov rsp, qword ptr gs:[0] swapgs sysretq @@ -179,6 +188,22 @@ unsafe extern "C" fn syscall_entry() { ); } +#[unsafe(no_mangle)] +unsafe extern "C" fn syscall_yield_check(_ctx: *mut UserContext) { + if let Some(pid) = current_pid() { + let needs_yield = { + let guard = safe_lock(|| SCHEDULER.lock()); + guard + .processes + .get(&pid) + .map_or(false, |p| !matches!(p.state, ProcessState::Running)) + }; + if needs_yield { + SCHEDULER.switch_next(true); + } + } +} + #[unsafe(naked)] #[unsafe(no_mangle)] pub unsafe fn run_next(ctx: *const UserContext, user_rsp: u64, user_cs: u64, user_ss: u64) { diff --git a/kernel/src/config.rs b/kernel/src/config.rs index 1a3ed00..82f0fdc 100644 --- a/kernel/src/config.rs +++ b/kernel/src/config.rs @@ -1,2 +1,2 @@ pub const TIMER_FREQUENCY_HZ: usize = 1000; -pub const KARCH: &str = "x86_64"; \ No newline at end of file +pub const KARCH: &str = "aarch64"; \ No newline at end of file diff --git a/kernel/src/driver/elf/loader.rs b/kernel/src/driver/elf/loader.rs index 75ee9b8..245e758 100644 --- a/kernel/src/driver/elf/loader.rs +++ b/kernel/src/driver/elf/loader.rs @@ -176,6 +176,8 @@ pub fn run_elf(file_bytes: &[u8], should_swapgs: bool, switch_to: bool) { #[cfg(target_arch = "aarch64")] SCHEDULER.with_process(process_pid, |process| { + use crate::task::context::UserContext; + process.saved_ctx = Some(UserContext { x0: 0, x1: 0, diff --git a/kernel/src/driver/framebuffer.rs b/kernel/src/driver/framebuffer.rs index 888fc28..b93a54f 100644 --- a/kernel/src/driver/framebuffer.rs +++ b/kernel/src/driver/framebuffer.rs @@ -63,7 +63,7 @@ impl Framebuffer { FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB, }, }; - const KERNEL_FB_BASE: u64 = 0xffffffffa0000000; + const KERNEL_FB_BASE: u64 = 0xffffffffc0000000; let buf_len = self.pitch * self.height; let byte_len = buf_len * core::mem::size_of::(); let pixel_frames = (byte_len + 4095) / 4096; diff --git a/kernel/src/driver/io/fs/assets.rs b/kernel/src/driver/io/fs/assets.rs index d070c71..f653487 100644 --- a/kernel/src/driver/io/fs/assets.rs +++ b/kernel/src/driver/io/fs/assets.rs @@ -1,6 +1,6 @@ -pub static INIT_ELF: &[u8] = include_bytes!("../../../../../assets/x86_64/init"); +pub static INIT_ELF: &[u8] = include_bytes!("../../../../../assets/aarch64/init"); pub static DOOM_WAD: &[u8] = include_bytes!("../../../../../assets/doom1.wad"); -pub static DOOM_ELF: &[u8] = include_bytes!("../../../../../assets/x86_64/doomgeneric"); -pub static HELLOWORLD_ELF: &[u8] = include_bytes!("../../../../../assets/x86_64/helloworld.elf"); -pub static BADAPPLE_ELF: &[u8] = include_bytes!("../../../../../assets/x86_64/badapple"); -pub static SHELL_ELF: &[u8] = include_bytes!("../../../../../assets/x86_64/shell"); \ No newline at end of file +pub static DOOM_ELF: &[u8] = include_bytes!("../../../../../assets/aarch64/doomgeneric"); +pub static HELLOWORLD_ELF: &[u8] = include_bytes!("../../../../../assets/aarch64/helloworld.elf"); +pub static BADAPPLE_ELF: &[u8] = include_bytes!("../../../../../assets/aarch64/badapple"); +pub static SHELL_ELF: &[u8] = include_bytes!("../../../../../assets/aarch64/shell"); \ No newline at end of file diff --git a/kernel/src/driver/io/input.rs b/kernel/src/driver/io/input.rs index 37f4704..f620cfd 100644 --- a/kernel/src/driver/io/input.rs +++ b/kernel/src/driver/io/input.rs @@ -139,7 +139,7 @@ pub fn process_input() { }; let mut scheduler = SCHEDULER.lock(); - for process in scheduler.processes.values_mut() { + if let Some(process) = scheduler.processes.get_mut(&1) { process.input_buffer.push(input_event); } drop(scheduler); diff --git a/kernel/src/driver/io/virtio/input.rs b/kernel/src/driver/io/virtio/input.rs index 261c6fc..8c1182b 100644 --- a/kernel/src/driver/io/virtio/input.rs +++ b/kernel/src/driver/io/virtio/input.rs @@ -112,10 +112,6 @@ pub fn input_interrupt(device_type: &str) { #[allow(static_mut_refs)] pub fn handle_event(event: &VirtioInputEvent) { - if event.event_type == 0 || event.code == 0 { - return; - } - enqueue_input_event(InputEvent { event_type: event.event_type, code: event.code, diff --git a/kernel/src/main.rs b/kernel/src/main.rs index e8c84de..d6b1cb9 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -155,6 +155,7 @@ unsafe extern "C" fn kmain() -> ! { kernel_main_x86_64() } + #[cfg(target_arch = "aarch64")] loop {} } else { kernel_crash() diff --git a/kernel/src/mm/shm.rs b/kernel/src/mm/shm.rs index be2ff4a..b076f0e 100644 --- a/kernel/src/mm/shm.rs +++ b/kernel/src/mm/shm.rs @@ -3,7 +3,7 @@ use core::sync::atomic::AtomicU64; use alloc::{collections::btree_map::BTreeMap, string::String, vec::Vec}; use spin::mutex::Mutex; -pub const USER_SHM_BASE: u64 = 0x0000_7000_0000_0000; +pub const USER_SHM_BASE: u64 = 0x0000_4000_0000_0000; pub static SHM_REGISTRY: Mutex>> = Mutex::new(None); pub static NEXT_SHM_ID: AtomicU64 = AtomicU64::new(1); pub const SHM_SLOT_SIZE: u64 = 64 * 1024 * 1024; diff --git a/kernel/src/task/process.rs b/kernel/src/task/process.rs index 322189f..b9abd50 100644 --- a/kernel/src/task/process.rs +++ b/kernel/src/task/process.rs @@ -15,7 +15,7 @@ pub enum ProcessState { } pub struct ProcessInfo { - pub exit_code: usize, + pub exit_code: isize, pub parent: usize, pub wake_tick: Option, } @@ -33,6 +33,7 @@ pub struct Process { pub user_entry: u64, pub last_switch_tick: u64, pub info: ProcessInfo, + pub in_ready_queue: bool, } impl Process { pub fn new( @@ -60,6 +61,7 @@ impl Process { parent: 0, wake_tick: None, }, + in_ready_queue: false, } } } diff --git a/kernel/src/task/scheduler.rs b/kernel/src/task/scheduler.rs index dc58b54..9530d2c 100644 --- a/kernel/src/task/scheduler.rs +++ b/kernel/src/task/scheduler.rs @@ -1,10 +1,7 @@ use core::sync::atomic::{AtomicU64, Ordering}; -use alloc::{collections::btree_map::BTreeMap, vec::Vec}; - use crate::{ arch::arch::{GLOBAL_TICK_COUNT, safe_lock}, - config::TIMER_FREQUENCY_HZ, driver::timer::TIMER, task::{ context::UserContext, @@ -12,6 +9,8 @@ use crate::{ }, util::Locked, }; +use alloc::collections::{binary_heap::BinaryHeap, btree_map::BTreeMap, vec_deque::VecDeque}; +use core::cmp::Reverse; #[cfg(target_arch = "aarch64")] use crate::arch::aarch64::interrupts::run_next; @@ -21,6 +20,7 @@ use crate::arch::x86_64::{ syscall::run_next, usermode::enter_usermode_x86_64, }; +use crate::task::context::ctx_save; #[cfg(target_arch = "x86_64")] use x86_64::structures::gdt::SegmentSelector; @@ -44,8 +44,16 @@ pub fn set_current_pid(pid: Option) { CURRENT_PID.store(pid.unwrap_or(0), Ordering::Relaxed); } +enum SwitchDecision { + Switch(Option, u64, u64), + Stay, + Idle, +} + pub struct Scheduler { pub processes: BTreeMap, + ready_queue: VecDeque, + pub sleep_queue: BinaryHeap>, next_pid: u64, } @@ -53,6 +61,8 @@ impl Scheduler { pub const fn new() -> Scheduler { Scheduler { processes: BTreeMap::new(), + ready_queue: VecDeque::new(), + sleep_queue: BinaryHeap::new(), next_pid: 1, } } @@ -79,96 +89,163 @@ impl Locked { ); guard.processes.insert(pid, process); + if let Some(new_process) = guard.processes.get_mut(&pid) { + new_process.in_ready_queue = true; + } + guard.ready_queue.push_back(pid); + Some(pid) } - pub fn next_task(&self) -> u64 { - if let Some(previous_pid) = current_pid() { - let mut guard = safe_lock(|| self.lock()); + pub fn switch_next(&self, should_swapgs: bool) -> bool { + let previous_pid = current_pid().unwrap_or(0); + if previous_pid == 0 { + return false; + } - for process in guard.processes.values_mut() { - if process.info.wake_tick.is_some() { - if TIMER.now().elapsed() >= process.info.wake_tick.unwrap() { - process.state = ProcessState::Ready; - process.info.wake_tick = None; + loop { + let decision = safe_lock(|| { + let mut guard = self.lock(); + self.wake_processes(&mut guard); + self.mark_old_process_ready(&mut guard); + + let mut chosen: Option = None; + while let Some(pid) = guard.ready_queue.pop_front() { + if let Some(process) = guard.processes.get_mut(&pid) { + process.in_ready_queue = false; + if matches!(process.state, ProcessState::Ready) { + chosen = Some(pid); + break; + } + }; + } + + let new_pid = match chosen { + Some(pid) => pid, + None => { + let is_prev_ready = matches!( + guard + .processes + .get(&previous_pid) + .map(|process| &process.state), + Some(ProcessState::Ready) + ); + + if is_prev_ready { + previous_pid + } else { + return SwitchDecision::Idle; + } + } + }; + + if current_pid() == Some(new_pid) { + if let Some(p) = guard.processes.get_mut(&new_pid) { + p.state = ProcessState::Running; + p.last_switch_tick = GLOBAL_TICK_COUNT.load(Ordering::Relaxed); + } + + return SwitchDecision::Stay; + } + + let new_process = guard + .processes + .get_mut(&new_pid) + .expect("Cant get new process"); + + new_process.state = ProcessState::Running; + new_process.last_switch_tick = GLOBAL_TICK_COUNT.load(Ordering::Relaxed); + new_process.in_ready_queue = false; + if let Some(address_space) = new_process.address_space.as_mut() { + address_space.use_address_space(); + } + + let ctx_opt = new_process.saved_ctx; + let entry = new_process.user_entry; + let stack_top = new_process.stack_top; + let kernel_stack_top = new_process.kernel_stack_top; + + set_current_pid(Some(new_pid)); + #[cfg(target_arch = "x86_64")] + unsafe { + use crate::arch::x86_64::{gdt::TSS_MUTEX, syscall::PER_CPU}; + use x86_64::VirtAddr; + + PER_CPU.kernel_rsp = kernel_stack_top; + if let Some(tss) = TSS_MUTEX.lock().as_mut() { + tss.privilege_stack_table[0] = VirtAddr::new(kernel_stack_top); + } + } + + #[cfg(target_arch = "aarch64")] + let _ = kernel_stack_top; + + SwitchDecision::Switch(ctx_opt, entry, stack_top) + }); + + match decision { + SwitchDecision::Switch(ctx_opt, entry, stack_top) => { + self.do_context_switch(ctx_opt, entry, stack_top, should_swapgs); + return true; + } + + SwitchDecision::Stay => return false, + + SwitchDecision::Idle => { + set_current_pid(None); + + #[cfg(target_arch = "x86_64")] + { + x86_64::instructions::interrupts::enable_and_hlt(); + x86_64::instructions::interrupts::disable(); + } + + #[cfg(target_arch = "aarch64")] + unsafe { + use aarch64_cpu::registers::{DAIF, Writeable}; + DAIF.write( + DAIF::D::Masked + DAIF::A::Masked + DAIF::I::Unmasked + DAIF::F::Masked, + ); + core::arch::asm!("wfi"); + DAIF.write( + DAIF::D::Masked + DAIF::A::Masked + DAIF::I::Masked + DAIF::F::Masked, + ); } } } + } + } - if let Some(process) = guard.processes.get_mut(&previous_pid) { - if matches!(process.state, ProcessState::Running) { + pub fn wake_processes(&self, guard: &mut Scheduler) { + let now = TIMER.now().elapsed(); + while let Some(&Reverse((wake_tick, pid))) = guard.sleep_queue.peek() { + if wake_tick > now { + break; + } + + guard.sleep_queue.pop(); + + if let Some(process) = guard.processes.get_mut(&pid) { + if process.info.wake_tick == Some(wake_tick) { process.state = ProcessState::Ready; + process.info.wake_tick = None; + if !process.in_ready_queue { + process.in_ready_queue = true; + guard.ready_queue.push_back(process.pid); + } } } - - let ready_pids: Vec = guard - .processes - .iter() - .filter(|(_, process)| matches!(process.state, ProcessState::Ready)) - .map(|(&pid, _)| pid) - .collect(); - - if ready_pids.is_empty() { - return previous_pid; - } - - let current_index = ready_pids.iter().position(|&pid| pid == previous_pid); - - return match current_index { - Some(i) => { - let next_index = (i + 1) % ready_pids.len(); - ready_pids[next_index] - } - None => ready_pids[0], - }; - } else { - panic!("Could not get current PID when switching to next task") - }; + } } #[allow(unused_variables)] - pub fn switch_to(&self, pid: u64, should_swapgs: bool) { - let (ctx_opt, entry, stack_top, kernel_stack_top) = { - let mut guard = safe_lock(|| self.lock()); - - if let Some(previous_pid) = current_pid() { - if let Some(old_process) = guard.processes.get_mut(&previous_pid) { - if matches!(old_process.state, ProcessState::Running) { - old_process.state = ProcessState::Ready; - } - } else { - // no previous process - } - } - - let new_process = guard.processes.get_mut(&pid).expect("Cant get new process"); - new_process.state = ProcessState::Running; - if let Some(address_space) = new_process.address_space.as_mut() { - address_space.use_address_space(); - }; - - ( - new_process.saved_ctx, - new_process.user_entry, - new_process.stack_top, - new_process.kernel_stack_top, - ) - }; - - set_current_pid(Some(pid)); - - #[cfg(target_arch = "x86_64")] - unsafe { - use x86_64::VirtAddr; - - use crate::arch::x86_64::{gdt::TSS_MUTEX, syscall::PER_CPU}; - - PER_CPU.kernel_rsp = kernel_stack_top; - if let Some(tss) = TSS_MUTEX.lock().as_mut() { - tss.privilege_stack_table[0] = VirtAddr::new(kernel_stack_top); - } - } - + fn do_context_switch( + &self, + ctx_opt: Option, + entry: u64, + stack_top: u64, + should_swapgs: bool, + ) { #[cfg(target_arch = "aarch64")] unsafe { let saved_ctx = ctx_opt.expect("Could not get user context"); @@ -193,6 +270,74 @@ impl Locked { } } + pub fn switch_to(&self, pid: u64, should_swapgs: bool) { + let switch_info = safe_lock(|| { + let mut guard = self.lock(); + self.wake_processes(&mut guard); + self.mark_old_process_ready(&mut guard); + let mut new_process = guard.processes.get_mut(&pid).expect("Cant get new process"); + let (ctx_opt, entry, stack_top, kernel_stack_top) = self.use_process(&mut new_process); + + set_current_pid(Some(pid)); + #[cfg(target_arch = "x86_64")] + unsafe { + use crate::arch::x86_64::{gdt::TSS_MUTEX, syscall::PER_CPU}; + use x86_64::VirtAddr; + + PER_CPU.kernel_rsp = kernel_stack_top; + if let Some(tss) = TSS_MUTEX.lock().as_mut() { + tss.privilege_stack_table[0] = VirtAddr::new(kernel_stack_top); + } + } + + Some((ctx_opt, entry, stack_top)) + }); + + if let Some((ctx_opt, entry, stack_top)) = switch_info { + self.do_context_switch(ctx_opt, entry, stack_top, should_swapgs); + } + } + + #[inline] + pub fn use_process(&self, new_process: &mut Process) -> (Option, u64, u64, u64) { + new_process.last_switch_tick = GLOBAL_TICK_COUNT.load(Ordering::Relaxed); + new_process.state = ProcessState::Running; + new_process.in_ready_queue = false; + if let Some(address_space) = new_process.address_space.as_mut() { + address_space.use_address_space(); + }; + + ( + new_process.saved_ctx, + new_process.user_entry, + new_process.stack_top, + new_process.kernel_stack_top, + ) + } + + pub fn mark_old_process_ready(&self, guard: &mut Scheduler) { + if let Some(previous_pid) = current_pid() { + if let Some(old_process) = guard.processes.get_mut(&previous_pid) { + if matches!(old_process.state, ProcessState::Running) && !old_process.in_ready_queue + { + old_process.state = ProcessState::Ready; + old_process.in_ready_queue = true; + guard.ready_queue.push_back(old_process.pid); + } + } + } + } + + pub fn terminate_process(&self, pid: u64, exit_code: isize) { + let mut guard = self.lock(); + if let Some(process) = guard.processes.get_mut(&pid) { + process.state = ProcessState::Zombie; + process.in_ready_queue = false; + process.input_buffer.clear(); + process.info.exit_code = exit_code; + } + } + pub fn with_process(&self, index: u64, f: F) -> Option where F: FnOnce(&mut Process) -> R, @@ -206,35 +351,28 @@ impl Locked { pub static SCHEDULER: Locked = Locked::new(Scheduler::new()); #[unsafe(no_mangle)] -pub extern "C" fn check_and_reschedule() -> isize { +pub extern "C" fn check_and_reschedule(ctx: &*mut UserContext) -> isize { let current_pid = CURRENT_PID.load(Ordering::Relaxed); let should = safe_lock(|| { let mut scheduler = SCHEDULER.lock(); + SCHEDULER.wake_processes(&mut scheduler); + if let Some(process) = scheduler.processes.get_mut(¤t_pid) { let elapsed = GLOBAL_TICK_COUNT.load(Ordering::Relaxed) - process.last_switch_tick; - if elapsed >= (TIMER_FREQUENCY_HZ / 60) as u64 { - process.last_switch_tick = GLOBAL_TICK_COUNT.load(Ordering::Relaxed); - true - } else { - false - } + elapsed >= 100 } else { false } }); if should { - let next_task = SCHEDULER.next_task(); - - if next_task == current_pid { - return 0; + ctx_save(*ctx); + match SCHEDULER.switch_next(true) { + true => return 1, + false => return 0, } - - SCHEDULER.switch_to(next_task, true); - - 1 - } else { - 0 } + + 0 } diff --git a/user/apps/doomgeneric b/user/apps/doomgeneric index a04df50..95694b7 160000 --- a/user/apps/doomgeneric +++ b/user/apps/doomgeneric @@ -1 +1 @@ -Subproject commit a04df50195107be3f5c6f906505faf9adabff90e +Subproject commit 95694b7b684b53a8f285af0078dab8e363470a0e diff --git a/user/apps/shell b/user/apps/shell index 0a27930..d8d08dc 160000 --- a/user/apps/shell +++ b/user/apps/shell @@ -1 +1 @@ -Subproject commit 0a27930fcf4f88b12c189b44a83f45c0d99c394b +Subproject commit d8d08dcf9f758a30e97bbc2d2c684fde439b8a57 diff --git a/user/init b/user/init index 5ed48db..007deba 160000 --- a/user/init +++ b/user/init @@ -1 +1 @@ -Subproject commit 5ed48dbd780a9c11de0dda933596a5bbb4a7e998 +Subproject commit 007debaadea68914beb7880368f5f910242ca3bf diff --git a/user/libxunil b/user/libxunil index 7f9192f..04e87c6 160000 --- a/user/libxunil +++ b/user/libxunil @@ -1 +1 @@ -Subproject commit 7f9192f4e8a57753da8c54829a9b6d36aa5851c9 +Subproject commit 04e87c6099c052b8f3e05b9e75da083e18268de8