#![allow(clippy::comparison_chain)] use super::{context::Context, cpu::Cpu, trapframe::Trapframe}; use crate::{ arch::riscv::{intr_get, Pagetable, PTE_W}, fs::file::{File, Inode}, mem::kalloc::kfree, sync::spinlock::{Spinlock, SpinlockGuard}, }; use core::{ ffi::{c_char, c_void}, ptr::{addr_of_mut, null_mut}, }; extern "C" { pub static mut proc: [Proc; crate::NPROC]; pub static mut initproc: *mut Proc; pub static mut nextpid: i32; pub static mut pid_lock: Spinlock; /// Helps ensure that wakeups of wait()ing /// parents are not lost. Helps obey the /// memory model when using p->parent. /// Must be acquired before any p->lock. pub static mut wait_lock: Spinlock; // trampoline.S pub static mut trampoline: *mut c_char; pub fn procinit(); pub fn userinit(); pub fn forkret(); pub fn fork() -> i32; pub fn exit(status: i32) -> !; pub fn wait(addr: u64) -> i32; pub fn procdump(); pub fn proc_mapstacks(kpgtbl: Pagetable); pub fn proc_pagetable(p: *mut Proc) -> Pagetable; pub fn proc_freepagetable(pagetable: Pagetable, sz: u64); pub fn wakeup(chan: *const c_void); pub fn allocproc() -> *mut Proc; // pub fn freeproc(p: *mut Proc); pub fn uvmalloc(pagetable: Pagetable, oldsz: u64, newsz: u64, xperm: i32) -> u64; pub fn uvmdealloc(pagetable: Pagetable, oldsz: u64, newsz: u64) -> u64; // pub fn sched(); pub fn scheduler() -> !; pub fn swtch(a: *mut Context, b: *mut Context); } #[repr(C)] #[derive(PartialEq, Default)] pub enum ProcState { #[default] Unused, Used, Sleeping, Runnable, Running, Zombie, } /// Per-process state. #[repr(C)] pub struct Proc { pub lock: Spinlock, // p->lock must be held when using these: /// Process state pub state: ProcState, /// If non-zero, sleeping on chan pub chan: *mut c_void, /// If non-zero, have been killed pub killed: i32, /// Exit status to be returned to parent's wait pub xstate: i32, /// Process ID pub pid: i32, // wait_lock msut be held when using this: /// Parent process pub parent: *mut Proc, // These are private to the process, so p->lock need not be held. /// Virtual address of kernel stack pub kstack: u64, /// Size of process memory (bytes) pub sz: u64, /// User page table pub pagetable: Pagetable, /// Data page for trampoline.S pub trapframe: *mut Trapframe, /// swtch() here to run process pub context: Context, /// Open files pub ofile: [*mut File; crate::NOFILE], /// Current directory pub cwd: *mut Inode, /// Process name (debugging) pub name: [c_char; 16], } impl Proc { pub const fn new() -> Proc { Proc { lock: Spinlock::new(), state: ProcState::Unused, chan: null_mut(), killed: 0, xstate: 0, pid: 0, parent: null_mut(), kstack: 0, sz: 0, pagetable: null_mut(), trapframe: null_mut(), context: Context::new(), ofile: [null_mut(); crate::NOFILE], cwd: null_mut(), name: [0x00; 16], } } pub fn current() -> Option<&'static mut Proc> { let _ = crate::trap::InterruptBlocker::new(); let p = Cpu::current().proc; if p.is_null() { None } else { unsafe { Some(&mut *p) } } } /// Kill the process with the given pid. /// Returns true if the process was killed. /// The victim won't exit until it tries to return /// to user space (see usertrap() in trap.c). pub unsafe fn kill(pid: i32) -> bool { for p in &mut proc { let _guard = p.lock.lock(); if p.pid == pid { p.killed = 1; if p.state == ProcState::Sleeping { // Wake process from sleep(). p.state = ProcState::Runnable; } return true; } } false } pub fn is_killed(&self) -> bool { let _guard = self.lock.lock(); self.killed > 0 } pub fn set_killed(&mut self, killed: bool) { let _guard = self.lock.lock(); if killed { self.killed = 1; } else { self.killed = 0; } } } /// Return the current struct proc *, or zero if none. #[no_mangle] pub unsafe extern "C" fn myproc() -> *mut Proc { if let Some(p) = Proc::current() { p as *mut Proc } else { null_mut() } } #[no_mangle] pub unsafe extern "C" fn allocpid() -> i32 { let _guard = pid_lock.lock(); let pid = nextpid; nextpid += 1; pid } /// Free a proc structure and the data hanging from it, including user pages. /// p->lock must be held. #[no_mangle] pub unsafe extern "C" fn freeproc(p: *mut Proc) { if !(*p).trapframe.is_null() { kfree((*p).trapframe.cast()); } (*p).trapframe = null_mut(); if !(*p).pagetable.is_null() { proc_freepagetable((*p).pagetable, (*p).sz); } (*p).pagetable = null_mut(); (*p).sz = 0; (*p).pid = 0; (*p).parent = null_mut(); (*p).name[0] = 0; (*p).chan = null_mut(); (*p).killed = 0; (*p).xstate = 0; (*p).state = ProcState::Unused; } /// Pass p's abandoned children to init. /// Caller must hold wait_lock. #[no_mangle] pub unsafe extern "C" fn reparent(p: *mut Proc) { for pp in proc.iter_mut().map(|p: &mut Proc| addr_of_mut!(*p)) { if (*pp).parent == p { (*pp).parent = initproc; wakeup(initproc.cast()); } } } /// Grow or shrink user memory by n bytes. /// Return 0 on success, -1 on failure. pub unsafe fn growproc(n: i32) -> i32 { let p = Proc::current().unwrap(); let mut sz = p.sz; if n > 0 { sz = uvmalloc(p.pagetable, sz, sz.wrapping_add(n as u64), PTE_W); if sz == 0 { return -1; } } else if n < 0 { sz = uvmdealloc(p.pagetable, sz, sz.wrapping_add(n as u64)); } p.sz = sz; 0 } /// Give up the CPU for one scheduling round. pub unsafe fn r#yield() { let p = Proc::current().unwrap(); let _guard = p.lock.lock(); p.state = ProcState::Runnable; sched(); } /// Switch to scheduler. Must hold only p->lock /// and have changed proc->state. Saves and restores /// previous_interrupts_enabled because previous_interrupts_enabled is a property of this /// kernel thread, not this CPU. It should /// be proc->previous_interrupts_enabled and proc->interrupt_disable_layers, but that would /// break in the few places where a lock is held but /// there's no process. #[no_mangle] pub unsafe extern "C" fn sched() { let p = Proc::current().unwrap(); let cpu = Cpu::current(); if cpu.interrupt_disable_layers != 1 { panic!("sched locks"); } else if p.state == ProcState::Running { panic!("sched running"); } else if intr_get() > 0 { panic!("sched interruptible"); } let previous_interrupts_enabled = cpu.previous_interrupts_enabled; swtch(addr_of_mut!(p.context), addr_of_mut!(cpu.context)); cpu.previous_interrupts_enabled = previous_interrupts_enabled; } /// The lock should already be locked. /// Unsafely create a new guard for it so that we can call SpinlockGuard.sleep(). #[no_mangle] pub unsafe extern "C" fn sleep_lock(chan: *mut c_void, lock: *mut Spinlock) { let lock: &Spinlock = &*lock; let guard = SpinlockGuard { lock }; guard.sleep(chan); core::mem::forget(guard); } /// Sleep until `wakeup(chan)` is called somewhere else. pub unsafe fn sleep(chan: *mut c_void) { let p = Proc::current().unwrap(); let _guard = p.lock.lock(); // Go to sleep. p.chan = chan; p.state = ProcState::Sleeping; sched(); // Tidy up. p.chan = null_mut(); } /// Kill the process with the given pid. /// The victim won't exit until it tries to return /// to user space (see usertrap() in trap.c). #[no_mangle] pub unsafe extern "C" fn kill(pid: i32) -> i32 { if Proc::kill(pid) { 1 } else { 0 } } #[no_mangle] pub unsafe extern "C" fn setkilled(p: *mut Proc) { (*p).set_killed(true); } #[no_mangle] pub unsafe extern "C" fn killed(p: *mut Proc) -> i32 { if (*p).is_killed() { 1 } else { 0 } }