1#![allow(dead_code)]
7#![allow(static_mut_refs)]
8extern crate alloc;
9use alloc::vec::Vec;
10use core::sync::atomic::{AtomicUsize, Ordering};
11use spin::Mutex;
12
13extern "C" {
14 fn switch_context(from_sp: *mut usize, to_sp: usize);
15 fn enter_userspace(user_entry: usize, user_sp: usize, kernel_sp: usize);
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum ProcessState {
21 Running,
23 Ready,
25 Waiting,
27 Terminated,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
33pub enum Priority {
34 Low = 0,
36 Normal = 1,
38 High = 2,
40}
41
42pub struct Process {
44 pub id: usize,
46 pub name: &'static str,
48 pub state: ProcessState,
50 pub priority: Priority,
52 pub stack_ptr: usize,
54 pub stack_mem: Vec<u8>,
56 pub wait_ticks: usize,
58 pub page_table_phys_addr: u64,
60 pub starvation_count: usize,
62 pub is_kernel: bool,
64 pub user_entry: usize,
66 pub user_stack_ptr: usize,
68 pub kernel_stack: Vec<u8>,
70 pub time_slice: usize,
72 pub need_resched: bool,
74}
75
76pub struct CoreRunqueue {
78 pub processes: Vec<Process>,
80 pub current_idx: Option<usize>,
82}
83
84static NEXT_PID: AtomicUsize = AtomicUsize::new(1);
86
87static RUNQUEUES: [Mutex<Option<CoreRunqueue>>; 4] = [
90 Mutex::new(None),
91 Mutex::new(None),
92 Mutex::new(None),
93 Mutex::new(None),
94];
95
96static ZOMBIES: [Mutex<Vec<Process>>; 4] = [
102 Mutex::new(Vec::new()),
103 Mutex::new(Vec::new()),
104 Mutex::new(Vec::new()),
105 Mutex::new(Vec::new()),
106];
107
108#[inline]
110fn current_sp() -> usize {
111 let sp: usize;
112 unsafe {
113 core::arch::asm!("mov {}, sp", out(reg) sp);
114 }
115 sp
116}
117
118static PREEMPT_COUNTS: [AtomicUsize; 4] = [
123 AtomicUsize::new(0),
124 AtomicUsize::new(0),
125 AtomicUsize::new(0),
126 AtomicUsize::new(0),
127];
128
129pub fn preempt_disable() {
131 let cid = core_id();
132 PREEMPT_COUNTS[cid].fetch_add(1, Ordering::SeqCst);
133}
134
135pub fn preempt_enable() {
138 let cid = core_id();
139 let old = PREEMPT_COUNTS[cid].fetch_sub(1, Ordering::SeqCst);
140 if old == 1 {
141 if should_preempt() {
143 yield_now();
144 }
145 }
146}
147
148pub fn is_preempt_enabled() -> bool {
150 let cid = core_id();
151 PREEMPT_COUNTS[cid].load(Ordering::SeqCst) == 0
152}
153
154pub fn should_preempt() -> bool {
156 if !is_preempt_enabled() {
157 return false;
158 }
159 let cid = core_id();
160 if let Some(rq_lock) = RUNQUEUES[cid].try_lock() {
162 if let Some(rq) = rq_lock.as_ref() {
163 if let Some(idx) = rq.current_idx {
164 if idx < rq.processes.len() {
165 return rq.processes[idx].need_resched;
166 }
167 }
168 }
169 }
170 false
171}
172
173pub fn scheduler_tick() {
175 let cid = core_id();
176 let Some(mut rq_lock) = RUNQUEUES[cid].try_lock() else {
194 return;
195 };
196 if let Some(rq) = rq_lock.as_mut() {
197 if let Some(idx) = rq.current_idx {
198 if idx < rq.processes.len() {
199 let p = &mut rq.processes[idx];
200 if p.id != 0 && p.name != "idle_core" {
201 if p.time_slice > 0 {
202 p.time_slice -= 1;
203 }
204 if p.time_slice == 0 {
205 p.need_resched = true;
206 }
207 }
208 }
209 }
210 }
211}
212
213fn stack_contains(mem: &[u8], sp: usize) -> bool {
215 let base = mem.as_ptr() as usize;
216 sp >= base && sp < base + mem.len()
217}
218
219fn reap_zombies(cid: usize) {
222 let sp = current_sp();
223 let mut z = ZOMBIES[cid].lock();
224 z.retain(|p| stack_contains(&p.kernel_stack, sp) || stack_contains(&p.stack_mem, sp));
225}
226
227#[inline]
230pub fn core_id() -> usize {
231 let mpidr: u64;
232 unsafe {
233 core::arch::asm!("mrs {}, mpidr_el1", out(reg) mpidr);
234 }
235 (mpidr & 0xFF) as usize
236}
237
238pub fn init() {
241 let current_ttbr0: u64;
242 unsafe {
243 core::arch::asm!("mrs {}, ttbr0_el1", out(reg) current_ttbr0);
244 }
245
246 let mut rq0 = RUNQUEUES[0].lock();
247 let processes = alloc::vec![Process {
248 id: 0,
249 name: "kernel_main",
250 state: ProcessState::Running,
251 priority: Priority::High,
252 stack_ptr: 0,
253 stack_mem: Vec::new(),
254 wait_ticks: 0,
255 page_table_phys_addr: current_ttbr0,
256 starvation_count: 0,
257 is_kernel: true,
258 user_entry: 0,
259 user_stack_ptr: 0,
260 kernel_stack: Vec::new(),
261 time_slice: 10,
262 need_resched: false,
263 }];
264
265 *rq0 = Some(CoreRunqueue {
266 processes,
267 current_idx: Some(0),
268 });
269
270 for i in 1..4 {
271 let mut rq = RUNQUEUES[i].lock();
272 *rq = Some(CoreRunqueue {
273 processes: Vec::new(),
274 current_idx: None,
275 });
276 }
277}
278
279pub fn spawn(entry: fn(), priority: Priority, name: &'static str) -> usize {
290 spawn_with_stack(entry, priority, name, 1024 * 1024)
291}
292
293pub const LARGE_STACK_SIZE: usize = 8 * 1024 * 1024;
298
299pub fn spawn_with_stack(entry: fn(), priority: Priority, name: &'static str, stack_size: usize) -> usize {
301 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
302
303 let (used, total) = crate::kernel::allocator::get_heap_stats();
305 let free = total.saturating_sub(used);
306 if free < stack_size + 16 * 1024 {
307 crate::println!("[scheduler] WARNING: Insufficient heap memory to spawn process '{}' (Free: {} KB, Need: {} KB)", name, free / 1024, stack_size / 1024);
308 return 0;
309 }
310
311 let stack_mem = alloc::vec![0u8; stack_size];
312
313 let stack_top = stack_mem.as_ptr() as usize + stack_size;
314 let aligned_top = stack_top & !0xF;
315 let initial_sp = aligned_top - 240;
316
317 let sp_ptr = initial_sp as *mut usize;
318 unsafe {
319 for j in 0..10 {
320 sp_ptr.add(j).write_volatile(0);
321 }
322 sp_ptr.add(10).write_volatile(0);
323 sp_ptr.add(11).write_volatile(entry as usize);
324 }
325
326 let pt_addr = crate::kernel::mmu::create_process_page_table();
327
328 let new_proc = Process {
329 id: pid,
330 name,
331 state: ProcessState::Ready,
332 priority,
333 stack_ptr: initial_sp,
334 stack_mem,
335 wait_ticks: 0,
336 page_table_phys_addr: pt_addr,
337 starvation_count: 0,
338 is_kernel: true,
339 user_entry: 0,
340 user_stack_ptr: 0,
341 kernel_stack: Vec::new(),
342 time_slice: 10,
343 need_resched: false,
344 };
345
346 let mut best_core = 0;
348 let mut min_procs = usize::MAX;
349
350 for i in 0..4 {
352 let rq_opt = RUNQUEUES[i].lock();
353 if let Some(rq) = rq_opt.as_ref() {
354 let count = rq.processes.len();
355 if count < min_procs {
356 min_procs = count;
357 best_core = i;
358 }
359 }
360 }
361
362 let mut rq_lock = RUNQUEUES[best_core].lock();
364 if let Some(rq) = rq_lock.as_mut() {
365 rq.processes.push(new_proc);
366 crate::debug!(
367 "[SCHED] Spawned process '{}' (PID: {}, Priority: {:?}) onto Core {}",
368 name,
369 pid,
370 priority,
371 best_core
372 );
373 }
374
375 pid
376}
377
378pub fn spawn_on_core(
389 entry: fn(),
390 priority: Priority,
391 name: &'static str,
392 target_core: usize,
393) -> usize {
394 spawn_on_core_with_stack(entry, priority, name, target_core, 1024 * 1024)
395}
396
397pub fn spawn_on_core_with_stack(
399 entry: fn(),
400 priority: Priority,
401 name: &'static str,
402 target_core: usize,
403 stack_size: usize,
404) -> usize {
405 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
406
407 let (used, total) = crate::kernel::allocator::get_heap_stats();
409 let free = total.saturating_sub(used);
410 if free < stack_size + 16 * 1024 {
411 crate::println!("[scheduler] WARNING: Insufficient heap memory to spawn process '{}' (Free: {} KB, Need: {} KB)", name, free / 1024, stack_size / 1024);
412 return 0;
413 }
414
415 let stack_mem = alloc::vec![0u8; stack_size];
416
417 let stack_top = stack_mem.as_ptr() as usize + stack_size;
418 let aligned_top = stack_top & !0xF;
419 let initial_sp = aligned_top - 240;
420
421 let sp_ptr = initial_sp as *mut usize;
422 unsafe {
423 for j in 0..10 {
424 sp_ptr.add(j).write_volatile(0);
425 }
426 sp_ptr.add(10).write_volatile(0);
427 sp_ptr.add(11).write_volatile(entry as usize);
428 }
429
430 let pt_addr = crate::kernel::mmu::create_process_page_table();
431
432 let new_proc = Process {
433 id: pid,
434 name,
435 state: ProcessState::Ready,
436 priority,
437 stack_ptr: initial_sp,
438 stack_mem,
439 wait_ticks: 0,
440 page_table_phys_addr: pt_addr,
441 starvation_count: 0,
442 is_kernel: true,
443 user_entry: 0,
444 user_stack_ptr: 0,
445 kernel_stack: Vec::new(),
446 time_slice: 10,
447 need_resched: false,
448 };
449
450 let core = target_core.min(3);
451 let mut rq_lock = RUNQUEUES[core].lock();
452 if let Some(rq) = rq_lock.as_mut() {
453 rq.processes.push(new_proc);
454 crate::debug!(
455 "[SCHED] Spawned process '{}' (PID: {}, Priority: {:?}) onto Core {}",
456 name,
457 pid,
458 priority,
459 core
460 );
461 }
462
463 pid
464}
465
466extern "C" fn trampoline() {
470 let entry_ptr: usize;
471 let arg: usize;
472 unsafe {
473 core::arch::asm!(
474 "mov {}, x19",
475 "mov {}, x20",
476 out(reg) entry_ptr,
477 out(reg) arg,
478 );
479 let entry: fn(usize) = core::mem::transmute(entry_ptr);
480 entry(arg);
481 }
482 exit();
483}
484
485pub fn spawn_with_arg(
497 entry: fn(usize),
498 arg: usize,
499 priority: Priority,
500 name: &'static str,
501) -> usize {
502 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
503
504 let stack_size = 1024 * 1024;
505
506 let (used, total) = crate::kernel::allocator::get_heap_stats();
508 let free = total.saturating_sub(used);
509 if free < stack_size + 16 * 1024 {
510 crate::println!("[scheduler] WARNING: Insufficient heap memory to spawn process '{}' (Free: {} KB, Need: {} KB)", name, free / 1024, stack_size / 1024);
511 return 0;
512 }
513
514 let stack_mem = alloc::vec![0u8; stack_size];
515
516 let stack_top = stack_mem.as_ptr() as usize + stack_size;
517 let aligned_top = stack_top & !0xF;
518 let initial_sp = aligned_top - 240; let sp_ptr = initial_sp as *mut usize;
521 unsafe {
522 sp_ptr.add(0).write_volatile(entry as usize); sp_ptr.add(1).write_volatile(arg); for j in 2..10 {
527 sp_ptr.add(j).write_volatile(0);
528 }
529 sp_ptr.add(10).write_volatile(0); sp_ptr
531 .add(11)
532 .write_volatile(trampoline as *const () as usize); }
534
535 let pt_addr = crate::kernel::mmu::create_process_page_table();
536
537 let new_proc = Process {
538 id: pid,
539 name,
540 state: ProcessState::Ready,
541 priority,
542 stack_ptr: initial_sp,
543 stack_mem,
544 wait_ticks: 0,
545 page_table_phys_addr: pt_addr,
546 starvation_count: 0,
547 is_kernel: true,
548 user_entry: 0,
549 user_stack_ptr: 0,
550 kernel_stack: Vec::new(),
551 time_slice: 10,
552 need_resched: false,
553 };
554
555 let mut best_core = 0;
556 let mut min_procs = usize::MAX;
557 for i in 0..4 {
558 let rq_opt = RUNQUEUES[i].lock();
559 if let Some(rq) = rq_opt.as_ref() {
560 let count = rq.processes.len();
561 if count < min_procs {
562 min_procs = count;
563 best_core = i;
564 }
565 }
566 }
567
568 let mut rq_lock = RUNQUEUES[best_core].lock();
569 if let Some(rq) = rq_lock.as_mut() {
570 rq.processes.push(new_proc);
571 crate::debug!(
572 "[SCHED] Spawned process '{}' with arg (PID: {}, Core {})",
573 name,
574 pid,
575 best_core
576 );
577 }
578
579 pid
580}
581
582pub fn spawn_with_arg_on_core(
585 entry: fn(usize),
586 arg: usize,
587 priority: Priority,
588 name: &'static str,
589 target_core: usize,
590) -> usize {
591 spawn_with_arg_on_core_with_stack(entry, arg, priority, name, target_core, 1024 * 1024)
592}
593
594pub fn spawn_with_arg_on_core_with_stack(
596 entry: fn(usize),
597 arg: usize,
598 priority: Priority,
599 name: &'static str,
600 target_core: usize,
601 stack_size: usize,
602) -> usize {
603 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
604
605 let (used, total) = crate::kernel::allocator::get_heap_stats();
607 let free = total.saturating_sub(used);
608 if free < stack_size + 16 * 1024 {
609 crate::println!("[scheduler] WARNING: Insufficient heap memory to spawn process '{}' (Free: {} KB, Need: {} KB)", name, free / 1024, stack_size / 1024);
610 return 0;
611 }
612
613 let stack_mem = alloc::vec![0u8; stack_size];
614
615 let stack_top = stack_mem.as_ptr() as usize + stack_size;
616 let aligned_top = stack_top & !0xF;
617 let initial_sp = aligned_top - 240; let sp_ptr = initial_sp as *mut usize;
620 unsafe {
621 sp_ptr.add(0).write_volatile(entry as usize); sp_ptr.add(1).write_volatile(arg); for j in 2..10 {
626 sp_ptr.add(j).write_volatile(0);
627 }
628 sp_ptr.add(10).write_volatile(0); sp_ptr
630 .add(11)
631 .write_volatile(trampoline as *const () as usize); }
633
634 let pt_addr = crate::kernel::mmu::create_process_page_table();
635
636 let new_proc = Process {
637 id: pid,
638 name,
639 state: ProcessState::Ready,
640 priority,
641 stack_ptr: initial_sp,
642 stack_mem,
643 wait_ticks: 0,
644 page_table_phys_addr: pt_addr,
645 starvation_count: 0,
646 is_kernel: true,
647 user_entry: 0,
648 user_stack_ptr: 0,
649 kernel_stack: Vec::new(),
650 time_slice: 10,
651 need_resched: false,
652 };
653
654 let core = target_core.min(3);
655 let mut rq_lock = RUNQUEUES[core].lock();
656 if let Some(rq) = rq_lock.as_mut() {
657 rq.processes.push(new_proc);
658 crate::debug!(
659 "[SCHED] Spawned process '{}' with arg (PID: {}, Core {})",
660 name,
661 pid,
662 core
663 );
664 }
665
666 pid
667}
668
669pub fn get_process_page_table(pid: usize) -> Option<u64> {
677 for i in 0..4 {
678 let rq_opt = RUNQUEUES[i].lock();
679 if let Some(rq) = rq_opt.as_ref() {
680 for p in &rq.processes {
681 if p.id == pid {
682 return Some(p.page_table_phys_addr);
683 }
684 }
685 }
686 }
687 None
688}
689
690pub fn get_current_page_table() -> Option<u64> {
695 let cid = core_id();
696 let rq_opt = RUNQUEUES[cid].lock();
697 if let Some(rq) = rq_opt.as_ref() {
698 if let Some(idx) = rq.current_idx {
699 return Some(rq.processes[idx].page_table_phys_addr);
700 }
701 }
702 None
703}
704
705pub fn tick() {
708 let cid = core_id();
709 {
710 let mut rq_opt = RUNQUEUES[cid].lock();
711 if let Some(rq) = rq_opt.as_mut() {
712 for p in &mut rq.processes {
713 if p.state == ProcessState::Waiting {
714 if p.wait_ticks > 0 {
715 p.wait_ticks -= 1;
716 }
717 if p.wait_ticks == 0 {
718 p.state = ProcessState::Ready;
719 }
720 }
721 }
722 }
723 }
724 schedule();
725}
726
727pub fn decrement_wait_ticks() {
730 let cid = core_id();
731 let mut rq_opt = RUNQUEUES[cid].lock();
732 if let Some(rq) = rq_opt.as_mut() {
733 for p in &mut rq.processes {
734 if p.state == ProcessState::Waiting {
735 if p.wait_ticks > 0 {
736 p.wait_ticks -= 1;
737 }
738 if p.wait_ticks == 0 {
739 p.state = ProcessState::Ready;
740 }
741 }
742 }
743 }
744}
745
746pub fn sleep(ticks: usize) {
751 if ticks == 0 {
752 return;
753 }
754 crate::kernel::interrupt::disable_interrupts();
755
756 let cid = core_id();
757 {
758 let mut rq_opt = RUNQUEUES[cid].lock();
759 if let Some(rq) = rq_opt.as_mut() {
760 if let Some(idx) = rq.current_idx {
761 let curr = &mut rq.processes[idx];
762 curr.state = ProcessState::Waiting;
763 curr.wait_ticks = ticks;
764 }
765 }
766 }
767
768 yield_now();
769 crate::kernel::interrupt::enable_interrupts();
770}
771
772pub fn yield_now() {
775 crate::kernel::interrupt::disable_interrupts();
776 schedule();
777 crate::kernel::interrupt::enable_interrupts();
778}
779
780pub(crate) fn schedule() {
784 let cid = core_id();
785 reap_zombies(cid);
787 let mut rq_opt = RUNQUEUES[cid].lock();
788 let Some(rq) = rq_opt.as_mut() else {
789 return;
790 };
791 if rq.processes.is_empty() {
792 return;
793 }
794
795 let old_idx = rq.current_idx;
796
797 if let Some(idx) = old_idx {
798 if rq.processes[idx].state == ProcessState::Running {
799 rq.processes[idx].state = ProcessState::Ready;
800 }
801 }
802
803 let mut best_idx = None;
804 let mut best_score = 0;
805
806 let num_proc = rq.processes.len();
807 let start_idx = old_idx.unwrap_or(0);
808
809 for i in 1..=num_proc {
821 let idx = (start_idx + i) % num_proc;
822 let p = &mut rq.processes[idx];
823 if p.state == ProcessState::Ready {
824 let score = (p.priority as usize * 10).saturating_add(p.starvation_count);
826 if best_idx.is_none() || score > best_score {
827 best_idx = Some(idx);
828 best_score = score;
829 }
830 p.starvation_count = p.starvation_count.saturating_add(1);
832 }
833 }
834
835 if let Some(new_idx) = best_idx {
836 rq.processes[new_idx].starvation_count = 0; rq.processes[new_idx].state = ProcessState::Running;
838 rq.processes[new_idx].time_slice = 10; rq.processes[new_idx].need_resched = false; rq.current_idx = Some(new_idx);
841
842 let new_pt = rq.processes[new_idx].page_table_phys_addr;
843
844 if let Some(o_idx) = old_idx {
845 if o_idx != new_idx {
846 let old_sp_ptr = &mut rq.processes[o_idx].stack_ptr as *mut usize;
847 let new_sp = rq.processes[new_idx].stack_ptr;
848 drop(rq_opt);
849
850 unsafe {
851 let cur_pt: u64;
852 core::arch::asm!("mrs {}, ttbr0_el1", out(reg) cur_pt);
853 if cur_pt != new_pt {
854 core::arch::asm!(
855 "msr ttbr0_el1, {}",
856 "isb",
857 "tlbi vmalle1is",
858 "dsb sy",
859 "isb",
860 in(reg) new_pt
861 );
862 }
863 switch_context(old_sp_ptr, new_sp);
864 }
865 }
866 } else {
867 let mut dummy_sp: usize = 0;
869 let new_sp = rq.processes[new_idx].stack_ptr;
870 drop(rq_opt);
871 unsafe {
872 let cur_pt: u64;
873 core::arch::asm!("mrs {}, ttbr0_el1", out(reg) cur_pt);
874 if cur_pt != new_pt {
875 core::arch::asm!(
876 "msr ttbr0_el1, {}",
877 "isb",
878 "tlbi vmalle1is",
879 "dsb sy",
880 "isb",
881 in(reg) new_pt
882 );
883 }
884 switch_context(&mut dummy_sp as *mut usize, new_sp);
885 }
886 }
887 } else {
888 if let Some(o_idx) = old_idx {
889 if rq.processes[o_idx].state == ProcessState::Ready {
890 rq.processes[o_idx].state = ProcessState::Running;
891 rq.processes[o_idx].time_slice = 10; rq.processes[o_idx].need_resched = false;
893 } else {
894 rq.current_idx = None;
895 }
896 }
897 }
898}
899
900pub fn exit() -> ! {
906 crate::kernel::interrupt::disable_interrupts();
907 let cid = core_id();
908 {
909 let mut rq_opt = RUNQUEUES[cid].lock();
910 if let Some(rq) = rq_opt.as_mut() {
911 if let Some(idx) = rq.current_idx {
912 let dead = rq.processes.remove(idx);
915 crate::info!(
916 "[SCHED] Process '{}' (PID: {}) exited on core {}.",
917 dead.name,
918 dead.id,
919 cid
920 );
921 ZOMBIES[cid].lock().push(dead);
922 rq.current_idx = None;
923 }
924 }
925 }
926
927 schedule();
928
929 loop {
930 unsafe {
931 core::arch::asm!("wfe");
932 }
933 }
934}
935
936pub fn spawn_user(entry: fn(), priority: Priority, name: &'static str) -> usize {
947 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
948
949 let user_stack_size = 256 * 1024;
951 let user_stack_mem = alloc::vec![0u8; user_stack_size];
952 let user_stack_top = user_stack_mem.as_ptr() as usize + user_stack_size;
953 let user_sp = user_stack_top & !0xF;
954
955 let kernel_stack_size = 64 * 1024;
957 let kernel_stack_mem = alloc::vec![0u8; kernel_stack_size];
958
959 let trampoline_stack_size = 32 * 1024;
962 let trampoline_stack_mem = alloc::vec![0u8; trampoline_stack_size];
963 let trampoline_stack_top = trampoline_stack_mem.as_ptr() as usize + trampoline_stack_size;
964 let aligned_top = trampoline_stack_top & !0xF;
965 let initial_sp = aligned_top - 240;
966
967 let sp_ptr = initial_sp as *mut usize;
968 unsafe {
969 let kernel_stack_top = kernel_stack_mem.as_ptr() as usize + kernel_stack_size;
971 sp_ptr.add(0).write_volatile(entry as usize); sp_ptr.add(1).write_volatile(user_sp); sp_ptr.add(2).write_volatile(kernel_stack_top & !0xF); for j in 3..10 {
975 sp_ptr.add(j).write_volatile(0);
976 }
977 sp_ptr.add(10).write_volatile(0); sp_ptr
979 .add(11)
980 .write_volatile(user_trampoline as *const () as usize); }
982
983 let pt_addr = crate::kernel::mmu::create_user_page_table();
984
985 let new_proc = Process {
986 id: pid,
987 name,
988 state: ProcessState::Ready,
989 priority,
990 stack_ptr: initial_sp,
991 stack_mem: user_stack_mem, wait_ticks: 0,
993 page_table_phys_addr: pt_addr,
994 starvation_count: 0,
995 is_kernel: false,
996 user_entry: entry as usize,
997 user_stack_ptr: user_sp,
998 kernel_stack: kernel_stack_mem,
999 time_slice: 10,
1000 need_resched: false,
1001 };
1002
1003 let mut best_core = 0;
1005 let mut min_procs = usize::MAX;
1006 for i in 0..4 {
1007 let rq_opt = RUNQUEUES[i].lock();
1008 if let Some(rq) = rq_opt.as_ref() {
1009 let count = rq.processes.len();
1010 if count < min_procs {
1011 min_procs = count;
1012 best_core = i;
1013 }
1014 }
1015 }
1016
1017 let mut rq_lock = RUNQUEUES[best_core].lock();
1018 if let Some(rq) = rq_lock.as_mut() {
1019 rq.processes.push(new_proc);
1020 crate::debug!(
1021 "[SCHED] Spawned USER process '{}' (PID: {}, Priority: {:?}) onto Core {} [EL0]",
1022 name,
1023 pid,
1024 priority,
1025 best_core
1026 );
1027 }
1028
1029 core::mem::forget(trampoline_stack_mem);
1031
1032 pid
1033}
1034
1035extern "C" fn user_trampoline() {
1039 let user_entry: usize;
1040 let user_sp: usize;
1041 let kernel_sp: usize;
1042 unsafe {
1043 core::arch::asm!(
1044 "mov {}, x19",
1045 "mov {}, x20",
1046 "mov {}, x21",
1047 out(reg) user_entry,
1048 out(reg) user_sp,
1049 out(reg) kernel_sp,
1050 );
1051 }
1052
1053 crate::info!(
1054 "[SCHED] Entering EL0 (entry=0x{:x}, user_sp=0x{:x}, kernel_sp=0x{:x})",
1055 user_entry,
1056 user_sp,
1057 kernel_sp
1058 );
1059
1060 crate::kernel::interrupt::enable_interrupts();
1062
1063 unsafe {
1065 enter_userspace(user_entry, user_sp, kernel_sp);
1066 }
1067
1068 crate::error!("[SCHED] ERROR - returned from enter_userspace!");
1070 exit();
1071}
1072
1073pub fn spawn_user_on_core(
1084 entry: fn(),
1085 priority: Priority,
1086 name: &'static str,
1087 target_core: usize,
1088) -> usize {
1089 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
1090
1091 let user_stack_size = 256 * 1024;
1093 let user_stack_mem = alloc::vec![0u8; user_stack_size];
1094 let user_stack_top = user_stack_mem.as_ptr() as usize + user_stack_size;
1095 let user_sp = user_stack_top & !0xF;
1096
1097 let kernel_stack_size = 64 * 1024;
1099 let kernel_stack_mem = alloc::vec![0u8; kernel_stack_size];
1100
1101 let trampoline_stack_size = 32 * 1024;
1103 let trampoline_stack_mem = alloc::vec![0u8; trampoline_stack_size];
1104 let trampoline_stack_top = trampoline_stack_mem.as_ptr() as usize + trampoline_stack_size;
1105 let aligned_top = trampoline_stack_top & !0xF;
1106 let initial_sp = aligned_top - 240;
1107
1108 let sp_ptr = initial_sp as *mut usize;
1109 unsafe {
1110 let kernel_stack_top = kernel_stack_mem.as_ptr() as usize + kernel_stack_size;
1111 sp_ptr.add(0).write_volatile(entry as usize); sp_ptr.add(1).write_volatile(user_sp); sp_ptr.add(2).write_volatile(kernel_stack_top & !0xF); for j in 3..10 {
1115 sp_ptr.add(j).write_volatile(0);
1116 }
1117 sp_ptr.add(10).write_volatile(0); sp_ptr
1119 .add(11)
1120 .write_volatile(user_trampoline as *const () as usize); }
1122
1123 let pt_addr = crate::kernel::mmu::create_user_page_table();
1124
1125 let new_proc = Process {
1126 id: pid,
1127 name,
1128 state: ProcessState::Ready,
1129 priority,
1130 stack_ptr: initial_sp,
1131 stack_mem: user_stack_mem,
1132 wait_ticks: 0,
1133 page_table_phys_addr: pt_addr,
1134 starvation_count: 0,
1135 is_kernel: false,
1136 user_entry: entry as usize,
1137 user_stack_ptr: user_sp,
1138 kernel_stack: kernel_stack_mem,
1139 time_slice: 10,
1140 need_resched: false,
1141 };
1142
1143 let core = target_core.min(3);
1144 let mut rq_lock = RUNQUEUES[core].lock();
1145 if let Some(rq) = rq_lock.as_mut() {
1146 rq.processes.push(new_proc);
1147 crate::debug!(
1148 "[SCHED] Spawned USER process '{}' (PID: {}, Priority: {:?}) onto Core {} [EL0]",
1149 name,
1150 pid,
1151 priority,
1152 core
1153 );
1154 }
1155
1156 core::mem::forget(trampoline_stack_mem);
1158
1159 pid
1160}
1161
1162pub fn register_idle_process(core_id: usize) {
1164 let current_ttbr0: u64;
1165 unsafe {
1166 core::arch::asm!("mrs {}, ttbr0_el1", out(reg) current_ttbr0);
1167 }
1168
1169 let mut rq_lock = RUNQUEUES[core_id].lock();
1170 if let Some(rq) = rq_lock.as_mut() {
1171 rq.processes.push(Process {
1172 id: 0,
1173 name: "idle_core",
1174 state: ProcessState::Running,
1175 priority: Priority::Low,
1176 stack_ptr: 0,
1177 stack_mem: Vec::new(),
1178 wait_ticks: 0,
1179 page_table_phys_addr: current_ttbr0,
1180 starvation_count: 0,
1181 is_kernel: true,
1182 user_entry: 0,
1183 user_stack_ptr: 0,
1184 kernel_stack: Vec::new(),
1185 time_slice: 10,
1186 need_resched: false,
1187 });
1188 rq.current_idx = Some(0);
1189 }
1190}
1191
1192pub fn is_current_process_critical() -> bool {
1195 let cid = core_id();
1196 if let Some(rq_lock) = RUNQUEUES[cid].try_lock() {
1197 if let Some(rq) = rq_lock.as_ref() {
1198 if let Some(idx) = rq.current_idx {
1199 if idx < rq.processes.len() {
1200 let p = &rq.processes[idx];
1201 return p.id == 0 || p.name == "gui_shell" || p.name == "idle_core";
1203 }
1204 }
1205 }
1206 }
1207 true
1210}
1211
1212pub fn get_current_process_name() -> Option<&'static str> {
1215 let cid = core_id();
1216 if let Some(rq_lock) = RUNQUEUES[cid].try_lock() {
1217 if let Some(rq) = rq_lock.as_ref() {
1218 if let Some(idx) = rq.current_idx {
1219 if idx < rq.processes.len() {
1220 return Some(rq.processes[idx].name);
1221 }
1222 }
1223 }
1224 }
1225 None
1226}
1227
1228pub fn current_stack_bounds() -> Option<(usize, usize)> {
1231 let sp: usize;
1232 unsafe { core::arch::asm!("mov {}, sp", out(reg) sp, options(nomem, nostack, preserves_flags)) };
1234 extern "C" {
1236 static __cpu0_stack_bottom: u8;
1237 static __stack_cpu0_top: u8;
1238 }
1239 let (b0, t0) = (
1240 core::ptr::addr_of!(__cpu0_stack_bottom) as usize,
1241 core::ptr::addr_of!(__stack_cpu0_top) as usize,
1242 );
1243 if sp > b0 && sp <= t0 {
1244 return Some((b0, t0));
1245 }
1246 let cid = core_id();
1247 let rq_lock = RUNQUEUES[cid].try_lock()?;
1248 let rq = rq_lock.as_ref()?;
1249 let p = rq.processes.get(rq.current_idx?)?;
1250 let lo = p.stack_mem.as_ptr() as usize;
1251 let hi = lo + p.stack_mem.len();
1252 if sp > lo && sp <= hi { Some((lo, hi)) } else { None }
1253}
1254
1255pub fn get_current_process_id() -> Option<usize> {
1258 let cid = core_id();
1259 if let Some(rq_lock) = RUNQUEUES[cid].try_lock() {
1260 if let Some(rq) = rq_lock.as_ref() {
1261 if let Some(idx) = rq.current_idx {
1262 if idx < rq.processes.len() {
1263 return Some(rq.processes[idx].id);
1264 }
1265 }
1266 }
1267 }
1268 None
1269}