318 lines
8.3 KiB
Rust

#![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
}
}