Skip to content

Commit f94ec8c

Browse files
committed
feat(kernel): integrate cooperative task scheduler
Updated kernel initialization to use global scheduler instance. Allocates proper 8KB stacks from heap for each task instead of using unmapped placeholder addresses, fixing page fault issues. Creates three tasks: - kernel_boot: placeholder for initial context (task 0) - idle: CPU-saving idle loop (task 1) - task1: demo task printing to serial (task 2) Tasks now call task_yield() to cooperatively switch between each other. Added delay loops and debug output to demonstrate task switching in action.
1 parent ca2b43b commit f94ec8c

1 file changed

Lines changed: 63 additions & 43 deletions

File tree

kernel/src/main.rs

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#![no_std]
22
#![no_main]
33

4+
extern crate alloc;
5+
46
use core::panic::PanicInfo;
57
use graphics::{draw_memory_map, fill_screen_blue};
68
use hal::serial_println;
79
use limine::request::{FramebufferRequest, MemoryMapRequest};
810
use limine::BaseRevision;
911
use memory::heap::{init_heap, StaticBootFrameAllocator};
10-
use task::{SchedClass, TaskCB, TaskManager};
12+
use task::{SchedClass, Scheduler, TaskCB, TaskManager};
1113
use util::panic::halt_loop;
1214
use x86_64::structures::paging::PhysFrame;
1315
use x86_64::{PhysAddr, VirtAddr};
@@ -17,16 +19,31 @@ static FRAMEBUFFER_REQ: FramebufferRequest = FramebufferRequest::new();
1719
static MMAP_REQ: MemoryMapRequest = MemoryMapRequest::new();
1820
static SCHEDULER: TaskManager = TaskManager::new();
1921

20-
fn idle_task() -> ! {
21-
serial_println!("Idle task running");
22+
/*unsafe extern "C" {
23+
fn context_switch(old: *mut CPUContext, new: *const CPUContext) -> !;
24+
}*/
25+
26+
unsafe extern "C" fn idle_task() -> ! {
27+
serial_println!("Idle task started!");
2228
loop {
23-
hal::cpu::halt();
29+
serial_println!("Idle task running");
30+
for _ in 0..10000000 {
31+
core::hint::spin_loop();
32+
}
33+
serial_println!("Idle task calling yield");
34+
task::task_yield();
35+
serial_println!("Idle task returned from yield");
2436
}
2537
}
26-
fn user_task_one() -> ! {
27-
serial_println!("User task one running");
38+
39+
unsafe extern "C" fn task1() -> ! {
40+
serial_println!("Task 1 started!");
2841
loop {
29-
hal::cpu::halt();
42+
serial_println!("Task 1 running");
43+
for _ in 0..10000000 {
44+
core::hint::spin_loop();
45+
}
46+
task::task_yield();
3047
}
3148
}
3249

@@ -58,22 +75,6 @@ pub extern "C" fn _start() -> ! {
5875
apic::timer::init_hardware(); // Initialize timer hardware
5976
}
6077

61-
let idle = TaskCB::new(
62-
"idle",
63-
VirtAddr::new(idle_task as *const () as u64),
64-
VirtAddr::new(0xFFFF_FF80_0000_1000),
65-
SchedClass::Fair(120),
66-
);
67-
let task1 = TaskCB::new(
68-
"idle",
69-
VirtAddr::new(user_task_one as *const () as u64),
70-
VirtAddr::new(0xFFFF_FF80_0000_2000),
71-
SchedClass::Fair(110),
72-
);
73-
74-
//SCHEDULER.add_task(idle);
75-
//SCHEDULER.add_task(task1);
76-
7778
//Access framebuffer info
7879
let fb_response = FRAMEBUFFER_REQ
7980
.get_response()
@@ -115,24 +116,43 @@ pub extern "C" fn _start() -> ! {
115116
fill_screen_blue(&fb);
116117
draw_memory_map(&fb, mmap_response.entries());
117118
}
118-
119-
loop {
120-
if let Some(task) = SCHEDULER.schedule() {
121-
serial_println!("Running task: {}", task.name);
122-
//simply call entr fn
123-
let task_fn: extern "C" fn() -> ! = unsafe { core::mem::transmute(task.context.rip) };
124-
task_fn();
125-
} else {
126-
// Print timer ticks every so often to show timer is working
127-
static mut LAST_TICK: u64 = 0;
128-
let current_tick = apic::timer::ticks();
129-
unsafe {
130-
if current_tick > LAST_TICK + 1000 {
131-
serial_println!("Timer ticks: {}", current_tick);
132-
LAST_TICK = current_tick;
133-
}
134-
}
135-
hal::cpu::halt();
136-
}
137-
}
119+
120+
// Initialize global scheduler
121+
Scheduler::init_global();
122+
{
123+
let mut scheduler = Scheduler::global().lock();
124+
125+
// Allocate proper stacks for tasks (8KB each)
126+
const STACK_SIZE: usize = 8192;
127+
128+
// Create kernel boot task - this represents the current execution context
129+
let kernel_stack_mem = alloc::vec![0u8; STACK_SIZE];
130+
let kernel_stack = VirtAddr::from_ptr(kernel_stack_mem.as_ptr()) + STACK_SIZE as u64;
131+
core::mem::forget(kernel_stack_mem);
132+
133+
let idle_stack_mem = alloc::vec![0u8; STACK_SIZE];
134+
let task1_stack_mem = alloc::vec![0u8; STACK_SIZE];
135+
136+
// Get stack top addresses (stacks grow downward)
137+
let idle_stack = VirtAddr::from_ptr(idle_stack_mem.as_ptr()) + STACK_SIZE as u64;
138+
let task1_stack = VirtAddr::from_ptr(task1_stack_mem.as_ptr()) + STACK_SIZE as u64;
139+
140+
// Prevent stack memory from being deallocated
141+
core::mem::forget(idle_stack_mem);
142+
core::mem::forget(task1_stack_mem);
143+
144+
// Add kernel boot task as first task (will never actually run, just holds boot context)
145+
let kernel_task = TaskCB::new("kernel_boot", idle_task, kernel_stack, SchedClass::Fair(0));
146+
scheduler.add_task(kernel_task);
147+
148+
let idle = TaskCB::new("idle", idle_task, idle_stack, SchedClass::Fair(120));
149+
let task1 = TaskCB::new("task1", task1, task1_stack, SchedClass::Fair(110));
150+
scheduler.add_task(idle);
151+
scheduler.add_task(task1);
152+
153+
serial_println!("Starting scheduler with {} tasks", scheduler.task_count());
154+
} // Lock is dropped here
155+
156+
// Jump into scheduler - this never returns
157+
unsafe { Scheduler::start(); }
138158
}

0 commit comments

Comments
 (0)