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 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
291
292 let stack_size = 1024 * 1024;
293
294 let (used, total) = crate::kernel::allocator::get_heap_stats();
296 let free = total.saturating_sub(used);
297 if free < stack_size + 16 * 1024 {
298 crate::println!("[scheduler] WARNING: Insufficient heap memory to spawn process '{}' (Free: {} KB, Need: {} KB)", name, free / 1024, stack_size / 1024);
299 return 0;
300 }
301
302 let stack_mem = alloc::vec![0u8; stack_size];
303
304 let stack_top = stack_mem.as_ptr() as usize + stack_size;
305 let aligned_top = stack_top & !0xF;
306 let initial_sp = aligned_top - 240;
307
308 let sp_ptr = initial_sp as *mut usize;
309 unsafe {
310 for j in 0..10 {
311 sp_ptr.add(j).write_volatile(0);
312 }
313 sp_ptr.add(10).write_volatile(0);
314 sp_ptr.add(11).write_volatile(entry as usize);
315 }
316
317 let pt_addr = crate::kernel::mmu::create_process_page_table();
318
319 let new_proc = Process {
320 id: pid,
321 name,
322 state: ProcessState::Ready,
323 priority,
324 stack_ptr: initial_sp,
325 stack_mem,
326 wait_ticks: 0,
327 page_table_phys_addr: pt_addr,
328 starvation_count: 0,
329 is_kernel: true,
330 user_entry: 0,
331 user_stack_ptr: 0,
332 kernel_stack: Vec::new(),
333 time_slice: 10,
334 need_resched: false,
335 };
336
337 let mut best_core = 0;
339 let mut min_procs = usize::MAX;
340
341 for i in 0..4 {
343 let rq_opt = RUNQUEUES[i].lock();
344 if let Some(rq) = rq_opt.as_ref() {
345 let count = rq.processes.len();
346 if count < min_procs {
347 min_procs = count;
348 best_core = i;
349 }
350 }
351 }
352
353 let mut rq_lock = RUNQUEUES[best_core].lock();
355 if let Some(rq) = rq_lock.as_mut() {
356 rq.processes.push(new_proc);
357 crate::debug!(
358 "[SCHED] Spawned process '{}' (PID: {}, Priority: {:?}) onto Core {}",
359 name,
360 pid,
361 priority,
362 best_core
363 );
364 }
365
366 pid
367}
368
369pub fn spawn_on_core(
380 entry: fn(),
381 priority: Priority,
382 name: &'static str,
383 target_core: usize,
384) -> usize {
385 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
386
387 let stack_size = 1024 * 1024;
388
389 let (used, total) = crate::kernel::allocator::get_heap_stats();
391 let free = total.saturating_sub(used);
392 if free < stack_size + 16 * 1024 {
393 crate::println!("[scheduler] WARNING: Insufficient heap memory to spawn process '{}' (Free: {} KB, Need: {} KB)", name, free / 1024, stack_size / 1024);
394 return 0;
395 }
396
397 let stack_mem = alloc::vec![0u8; stack_size];
398
399 let stack_top = stack_mem.as_ptr() as usize + stack_size;
400 let aligned_top = stack_top & !0xF;
401 let initial_sp = aligned_top - 240;
402
403 let sp_ptr = initial_sp as *mut usize;
404 unsafe {
405 for j in 0..10 {
406 sp_ptr.add(j).write_volatile(0);
407 }
408 sp_ptr.add(10).write_volatile(0);
409 sp_ptr.add(11).write_volatile(entry as usize);
410 }
411
412 let pt_addr = crate::kernel::mmu::create_process_page_table();
413
414 let new_proc = Process {
415 id: pid,
416 name,
417 state: ProcessState::Ready,
418 priority,
419 stack_ptr: initial_sp,
420 stack_mem,
421 wait_ticks: 0,
422 page_table_phys_addr: pt_addr,
423 starvation_count: 0,
424 is_kernel: true,
425 user_entry: 0,
426 user_stack_ptr: 0,
427 kernel_stack: Vec::new(),
428 time_slice: 10,
429 need_resched: false,
430 };
431
432 let core = target_core.min(3);
433 let mut rq_lock = RUNQUEUES[core].lock();
434 if let Some(rq) = rq_lock.as_mut() {
435 rq.processes.push(new_proc);
436 crate::debug!(
437 "[SCHED] Spawned process '{}' (PID: {}, Priority: {:?}) onto Core {}",
438 name,
439 pid,
440 priority,
441 core
442 );
443 }
444
445 pid
446}
447
448extern "C" fn trampoline() {
452 let entry_ptr: usize;
453 let arg: usize;
454 unsafe {
455 core::arch::asm!(
456 "mov {}, x19",
457 "mov {}, x20",
458 out(reg) entry_ptr,
459 out(reg) arg,
460 );
461 let entry: fn(usize) = core::mem::transmute(entry_ptr);
462 entry(arg);
463 }
464 exit();
465}
466
467pub fn spawn_with_arg(
479 entry: fn(usize),
480 arg: usize,
481 priority: Priority,
482 name: &'static str,
483) -> usize {
484 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
485
486 let stack_size = 1024 * 1024;
487
488 let (used, total) = crate::kernel::allocator::get_heap_stats();
490 let free = total.saturating_sub(used);
491 if free < stack_size + 16 * 1024 {
492 crate::println!("[scheduler] WARNING: Insufficient heap memory to spawn process '{}' (Free: {} KB, Need: {} KB)", name, free / 1024, stack_size / 1024);
493 return 0;
494 }
495
496 let stack_mem = alloc::vec![0u8; stack_size];
497
498 let stack_top = stack_mem.as_ptr() as usize + stack_size;
499 let aligned_top = stack_top & !0xF;
500 let initial_sp = aligned_top - 240; let sp_ptr = initial_sp as *mut usize;
503 unsafe {
504 sp_ptr.add(0).write_volatile(entry as usize); sp_ptr.add(1).write_volatile(arg); for j in 2..10 {
509 sp_ptr.add(j).write_volatile(0);
510 }
511 sp_ptr.add(10).write_volatile(0); sp_ptr
513 .add(11)
514 .write_volatile(trampoline as *const () as usize); }
516
517 let pt_addr = crate::kernel::mmu::create_process_page_table();
518
519 let new_proc = Process {
520 id: pid,
521 name,
522 state: ProcessState::Ready,
523 priority,
524 stack_ptr: initial_sp,
525 stack_mem,
526 wait_ticks: 0,
527 page_table_phys_addr: pt_addr,
528 starvation_count: 0,
529 is_kernel: true,
530 user_entry: 0,
531 user_stack_ptr: 0,
532 kernel_stack: Vec::new(),
533 time_slice: 10,
534 need_resched: false,
535 };
536
537 let mut best_core = 0;
538 let mut min_procs = usize::MAX;
539 for i in 0..4 {
540 let rq_opt = RUNQUEUES[i].lock();
541 if let Some(rq) = rq_opt.as_ref() {
542 let count = rq.processes.len();
543 if count < min_procs {
544 min_procs = count;
545 best_core = i;
546 }
547 }
548 }
549
550 let mut rq_lock = RUNQUEUES[best_core].lock();
551 if let Some(rq) = rq_lock.as_mut() {
552 rq.processes.push(new_proc);
553 crate::debug!(
554 "[SCHED] Spawned process '{}' with arg (PID: {}, Core {})",
555 name,
556 pid,
557 best_core
558 );
559 }
560
561 pid
562}
563
564pub fn spawn_with_arg_on_core(
567 entry: fn(usize),
568 arg: usize,
569 priority: Priority,
570 name: &'static str,
571 target_core: usize,
572) -> usize {
573 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
574
575 let stack_size = 1024 * 1024;
576
577 let (used, total) = crate::kernel::allocator::get_heap_stats();
579 let free = total.saturating_sub(used);
580 if free < stack_size + 16 * 1024 {
581 crate::println!("[scheduler] WARNING: Insufficient heap memory to spawn process '{}' (Free: {} KB, Need: {} KB)", name, free / 1024, stack_size / 1024);
582 return 0;
583 }
584
585 let stack_mem = alloc::vec![0u8; stack_size];
586
587 let stack_top = stack_mem.as_ptr() as usize + stack_size;
588 let aligned_top = stack_top & !0xF;
589 let initial_sp = aligned_top - 240; let sp_ptr = initial_sp as *mut usize;
592 unsafe {
593 sp_ptr.add(0).write_volatile(entry as usize); sp_ptr.add(1).write_volatile(arg); for j in 2..10 {
598 sp_ptr.add(j).write_volatile(0);
599 }
600 sp_ptr.add(10).write_volatile(0); sp_ptr
602 .add(11)
603 .write_volatile(trampoline as *const () as usize); }
605
606 let pt_addr = crate::kernel::mmu::create_process_page_table();
607
608 let new_proc = Process {
609 id: pid,
610 name,
611 state: ProcessState::Ready,
612 priority,
613 stack_ptr: initial_sp,
614 stack_mem,
615 wait_ticks: 0,
616 page_table_phys_addr: pt_addr,
617 starvation_count: 0,
618 is_kernel: true,
619 user_entry: 0,
620 user_stack_ptr: 0,
621 kernel_stack: Vec::new(),
622 time_slice: 10,
623 need_resched: false,
624 };
625
626 let core = target_core.min(3);
627 let mut rq_lock = RUNQUEUES[core].lock();
628 if let Some(rq) = rq_lock.as_mut() {
629 rq.processes.push(new_proc);
630 crate::debug!(
631 "[SCHED] Spawned process '{}' with arg (PID: {}, Core {})",
632 name,
633 pid,
634 core
635 );
636 }
637
638 pid
639}
640
641pub fn get_process_page_table(pid: usize) -> Option<u64> {
649 for i in 0..4 {
650 let rq_opt = RUNQUEUES[i].lock();
651 if let Some(rq) = rq_opt.as_ref() {
652 for p in &rq.processes {
653 if p.id == pid {
654 return Some(p.page_table_phys_addr);
655 }
656 }
657 }
658 }
659 None
660}
661
662pub fn get_current_page_table() -> Option<u64> {
667 let cid = core_id();
668 let rq_opt = RUNQUEUES[cid].lock();
669 if let Some(rq) = rq_opt.as_ref() {
670 if let Some(idx) = rq.current_idx {
671 return Some(rq.processes[idx].page_table_phys_addr);
672 }
673 }
674 None
675}
676
677pub fn tick() {
680 let cid = core_id();
681 {
682 let mut rq_opt = RUNQUEUES[cid].lock();
683 if let Some(rq) = rq_opt.as_mut() {
684 for p in &mut rq.processes {
685 if p.state == ProcessState::Waiting {
686 if p.wait_ticks > 0 {
687 p.wait_ticks -= 1;
688 }
689 if p.wait_ticks == 0 {
690 p.state = ProcessState::Ready;
691 }
692 }
693 }
694 }
695 }
696 schedule();
697}
698
699pub fn decrement_wait_ticks() {
702 let cid = core_id();
703 let mut rq_opt = RUNQUEUES[cid].lock();
704 if let Some(rq) = rq_opt.as_mut() {
705 for p in &mut rq.processes {
706 if p.state == ProcessState::Waiting {
707 if p.wait_ticks > 0 {
708 p.wait_ticks -= 1;
709 }
710 if p.wait_ticks == 0 {
711 p.state = ProcessState::Ready;
712 }
713 }
714 }
715 }
716}
717
718pub fn sleep(ticks: usize) {
723 if ticks == 0 {
724 return;
725 }
726 crate::kernel::interrupt::disable_interrupts();
727
728 let cid = core_id();
729 {
730 let mut rq_opt = RUNQUEUES[cid].lock();
731 if let Some(rq) = rq_opt.as_mut() {
732 if let Some(idx) = rq.current_idx {
733 let curr = &mut rq.processes[idx];
734 curr.state = ProcessState::Waiting;
735 curr.wait_ticks = ticks;
736 }
737 }
738 }
739
740 yield_now();
741 crate::kernel::interrupt::enable_interrupts();
742}
743
744pub fn yield_now() {
747 crate::kernel::interrupt::disable_interrupts();
748 schedule();
749 crate::kernel::interrupt::enable_interrupts();
750}
751
752pub(crate) fn schedule() {
756 let cid = core_id();
757 reap_zombies(cid);
759 let mut rq_opt = RUNQUEUES[cid].lock();
760 let Some(rq) = rq_opt.as_mut() else {
761 return;
762 };
763 if rq.processes.is_empty() {
764 return;
765 }
766
767 let old_idx = rq.current_idx;
768
769 if let Some(idx) = old_idx {
770 if rq.processes[idx].state == ProcessState::Running {
771 rq.processes[idx].state = ProcessState::Ready;
772 }
773 }
774
775 let mut best_idx = None;
776 let mut best_score = 0;
777
778 let num_proc = rq.processes.len();
779 let start_idx = old_idx.unwrap_or(0);
780
781 for i in 1..=num_proc {
793 let idx = (start_idx + i) % num_proc;
794 let p = &mut rq.processes[idx];
795 if p.state == ProcessState::Ready {
796 let score = (p.priority as usize * 10).saturating_add(p.starvation_count);
798 if best_idx.is_none() || score > best_score {
799 best_idx = Some(idx);
800 best_score = score;
801 }
802 p.starvation_count = p.starvation_count.saturating_add(1);
804 }
805 }
806
807 if let Some(new_idx) = best_idx {
808 rq.processes[new_idx].starvation_count = 0; rq.processes[new_idx].state = ProcessState::Running;
810 rq.processes[new_idx].time_slice = 10; rq.processes[new_idx].need_resched = false; rq.current_idx = Some(new_idx);
813
814 let new_pt = rq.processes[new_idx].page_table_phys_addr;
815
816 if let Some(o_idx) = old_idx {
817 if o_idx != new_idx {
818 let old_sp_ptr = &mut rq.processes[o_idx].stack_ptr as *mut usize;
819 let new_sp = rq.processes[new_idx].stack_ptr;
820 drop(rq_opt);
821
822 unsafe {
823 let cur_pt: u64;
824 core::arch::asm!("mrs {}, ttbr0_el1", out(reg) cur_pt);
825 if cur_pt != new_pt {
826 core::arch::asm!(
827 "msr ttbr0_el1, {}",
828 "isb",
829 "tlbi vmalle1is",
830 "dsb sy",
831 "isb",
832 in(reg) new_pt
833 );
834 }
835 switch_context(old_sp_ptr, new_sp);
836 }
837 }
838 } else {
839 let mut dummy_sp: usize = 0;
841 let new_sp = rq.processes[new_idx].stack_ptr;
842 drop(rq_opt);
843 unsafe {
844 let cur_pt: u64;
845 core::arch::asm!("mrs {}, ttbr0_el1", out(reg) cur_pt);
846 if cur_pt != new_pt {
847 core::arch::asm!(
848 "msr ttbr0_el1, {}",
849 "isb",
850 "tlbi vmalle1is",
851 "dsb sy",
852 "isb",
853 in(reg) new_pt
854 );
855 }
856 switch_context(&mut dummy_sp as *mut usize, new_sp);
857 }
858 }
859 } else {
860 if let Some(o_idx) = old_idx {
861 if rq.processes[o_idx].state == ProcessState::Ready {
862 rq.processes[o_idx].state = ProcessState::Running;
863 rq.processes[o_idx].time_slice = 10; rq.processes[o_idx].need_resched = false;
865 } else {
866 rq.current_idx = None;
867 }
868 }
869 }
870}
871
872pub fn exit() -> ! {
878 crate::kernel::interrupt::disable_interrupts();
879 let cid = core_id();
880 {
881 let mut rq_opt = RUNQUEUES[cid].lock();
882 if let Some(rq) = rq_opt.as_mut() {
883 if let Some(idx) = rq.current_idx {
884 let dead = rq.processes.remove(idx);
887 crate::info!(
888 "[SCHED] Process '{}' (PID: {}) exited on core {}.",
889 dead.name,
890 dead.id,
891 cid
892 );
893 ZOMBIES[cid].lock().push(dead);
894 rq.current_idx = None;
895 }
896 }
897 }
898
899 schedule();
900
901 loop {
902 unsafe {
903 core::arch::asm!("wfe");
904 }
905 }
906}
907
908pub fn spawn_user(entry: fn(), priority: Priority, name: &'static str) -> usize {
919 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
920
921 let user_stack_size = 256 * 1024;
923 let user_stack_mem = alloc::vec![0u8; user_stack_size];
924 let user_stack_top = user_stack_mem.as_ptr() as usize + user_stack_size;
925 let user_sp = user_stack_top & !0xF;
926
927 let kernel_stack_size = 64 * 1024;
929 let kernel_stack_mem = alloc::vec![0u8; kernel_stack_size];
930
931 let trampoline_stack_size = 32 * 1024;
934 let trampoline_stack_mem = alloc::vec![0u8; trampoline_stack_size];
935 let trampoline_stack_top = trampoline_stack_mem.as_ptr() as usize + trampoline_stack_size;
936 let aligned_top = trampoline_stack_top & !0xF;
937 let initial_sp = aligned_top - 240;
938
939 let sp_ptr = initial_sp as *mut usize;
940 unsafe {
941 let kernel_stack_top = kernel_stack_mem.as_ptr() as usize + kernel_stack_size;
943 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 {
947 sp_ptr.add(j).write_volatile(0);
948 }
949 sp_ptr.add(10).write_volatile(0); sp_ptr
951 .add(11)
952 .write_volatile(user_trampoline as *const () as usize); }
954
955 let pt_addr = crate::kernel::mmu::create_user_page_table();
956
957 let new_proc = Process {
958 id: pid,
959 name,
960 state: ProcessState::Ready,
961 priority,
962 stack_ptr: initial_sp,
963 stack_mem: user_stack_mem, wait_ticks: 0,
965 page_table_phys_addr: pt_addr,
966 starvation_count: 0,
967 is_kernel: false,
968 user_entry: entry as usize,
969 user_stack_ptr: user_sp,
970 kernel_stack: kernel_stack_mem,
971 time_slice: 10,
972 need_resched: false,
973 };
974
975 let mut best_core = 0;
977 let mut min_procs = usize::MAX;
978 for i in 0..4 {
979 let rq_opt = RUNQUEUES[i].lock();
980 if let Some(rq) = rq_opt.as_ref() {
981 let count = rq.processes.len();
982 if count < min_procs {
983 min_procs = count;
984 best_core = i;
985 }
986 }
987 }
988
989 let mut rq_lock = RUNQUEUES[best_core].lock();
990 if let Some(rq) = rq_lock.as_mut() {
991 rq.processes.push(new_proc);
992 crate::debug!(
993 "[SCHED] Spawned USER process '{}' (PID: {}, Priority: {:?}) onto Core {} [EL0]",
994 name,
995 pid,
996 priority,
997 best_core
998 );
999 }
1000
1001 core::mem::forget(trampoline_stack_mem);
1003
1004 pid
1005}
1006
1007extern "C" fn user_trampoline() {
1011 let user_entry: usize;
1012 let user_sp: usize;
1013 let kernel_sp: usize;
1014 unsafe {
1015 core::arch::asm!(
1016 "mov {}, x19",
1017 "mov {}, x20",
1018 "mov {}, x21",
1019 out(reg) user_entry,
1020 out(reg) user_sp,
1021 out(reg) kernel_sp,
1022 );
1023 }
1024
1025 crate::info!(
1026 "[SCHED] Entering EL0 (entry=0x{:x}, user_sp=0x{:x}, kernel_sp=0x{:x})",
1027 user_entry,
1028 user_sp,
1029 kernel_sp
1030 );
1031
1032 crate::kernel::interrupt::enable_interrupts();
1034
1035 unsafe {
1037 enter_userspace(user_entry, user_sp, kernel_sp);
1038 }
1039
1040 crate::error!("[SCHED] ERROR - returned from enter_userspace!");
1042 exit();
1043}
1044
1045pub fn spawn_user_on_core(
1056 entry: fn(),
1057 priority: Priority,
1058 name: &'static str,
1059 target_core: usize,
1060) -> usize {
1061 let pid = NEXT_PID.fetch_add(1, Ordering::SeqCst);
1062
1063 let user_stack_size = 256 * 1024;
1065 let user_stack_mem = alloc::vec![0u8; user_stack_size];
1066 let user_stack_top = user_stack_mem.as_ptr() as usize + user_stack_size;
1067 let user_sp = user_stack_top & !0xF;
1068
1069 let kernel_stack_size = 64 * 1024;
1071 let kernel_stack_mem = alloc::vec![0u8; kernel_stack_size];
1072
1073 let trampoline_stack_size = 32 * 1024;
1075 let trampoline_stack_mem = alloc::vec![0u8; trampoline_stack_size];
1076 let trampoline_stack_top = trampoline_stack_mem.as_ptr() as usize + trampoline_stack_size;
1077 let aligned_top = trampoline_stack_top & !0xF;
1078 let initial_sp = aligned_top - 240;
1079
1080 let sp_ptr = initial_sp as *mut usize;
1081 unsafe {
1082 let kernel_stack_top = kernel_stack_mem.as_ptr() as usize + kernel_stack_size;
1083 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 {
1087 sp_ptr.add(j).write_volatile(0);
1088 }
1089 sp_ptr.add(10).write_volatile(0); sp_ptr
1091 .add(11)
1092 .write_volatile(user_trampoline as *const () as usize); }
1094
1095 let pt_addr = crate::kernel::mmu::create_user_page_table();
1096
1097 let new_proc = Process {
1098 id: pid,
1099 name,
1100 state: ProcessState::Ready,
1101 priority,
1102 stack_ptr: initial_sp,
1103 stack_mem: user_stack_mem,
1104 wait_ticks: 0,
1105 page_table_phys_addr: pt_addr,
1106 starvation_count: 0,
1107 is_kernel: false,
1108 user_entry: entry as usize,
1109 user_stack_ptr: user_sp,
1110 kernel_stack: kernel_stack_mem,
1111 time_slice: 10,
1112 need_resched: false,
1113 };
1114
1115 let core = target_core.min(3);
1116 let mut rq_lock = RUNQUEUES[core].lock();
1117 if let Some(rq) = rq_lock.as_mut() {
1118 rq.processes.push(new_proc);
1119 crate::debug!(
1120 "[SCHED] Spawned USER process '{}' (PID: {}, Priority: {:?}) onto Core {} [EL0]",
1121 name,
1122 pid,
1123 priority,
1124 core
1125 );
1126 }
1127
1128 core::mem::forget(trampoline_stack_mem);
1130
1131 pid
1132}
1133
1134pub fn register_idle_process(core_id: usize) {
1136 let current_ttbr0: u64;
1137 unsafe {
1138 core::arch::asm!("mrs {}, ttbr0_el1", out(reg) current_ttbr0);
1139 }
1140
1141 let mut rq_lock = RUNQUEUES[core_id].lock();
1142 if let Some(rq) = rq_lock.as_mut() {
1143 rq.processes.push(Process {
1144 id: 0,
1145 name: "idle_core",
1146 state: ProcessState::Running,
1147 priority: Priority::Low,
1148 stack_ptr: 0,
1149 stack_mem: Vec::new(),
1150 wait_ticks: 0,
1151 page_table_phys_addr: current_ttbr0,
1152 starvation_count: 0,
1153 is_kernel: true,
1154 user_entry: 0,
1155 user_stack_ptr: 0,
1156 kernel_stack: Vec::new(),
1157 time_slice: 10,
1158 need_resched: false,
1159 });
1160 rq.current_idx = Some(0);
1161 }
1162}
1163
1164pub fn is_current_process_critical() -> bool {
1167 let cid = core_id();
1168 if let Some(rq_lock) = RUNQUEUES[cid].try_lock() {
1169 if let Some(rq) = rq_lock.as_ref() {
1170 if let Some(idx) = rq.current_idx {
1171 if idx < rq.processes.len() {
1172 let p = &rq.processes[idx];
1173 return p.id == 0 || p.name == "gui_shell" || p.name == "idle_core";
1175 }
1176 }
1177 }
1178 }
1179 true
1182}
1183
1184pub fn get_current_process_name() -> Option<&'static str> {
1187 let cid = core_id();
1188 if let Some(rq_lock) = RUNQUEUES[cid].try_lock() {
1189 if let Some(rq) = rq_lock.as_ref() {
1190 if let Some(idx) = rq.current_idx {
1191 if idx < rq.processes.len() {
1192 return Some(rq.processes[idx].name);
1193 }
1194 }
1195 }
1196 }
1197 None
1198}
1199
1200pub fn get_current_process_id() -> Option<usize> {
1203 let cid = core_id();
1204 if let Some(rq_lock) = RUNQUEUES[cid].try_lock() {
1205 if let Some(rq) = rq_lock.as_ref() {
1206 if let Some(idx) = rq.current_idx {
1207 if idx < rq.processes.len() {
1208 return Some(rq.processes[idx].id);
1209 }
1210 }
1211 }
1212 }
1213 None
1214}