diff --git a/src/kernel/src/arch/mod.rs b/src/kernel/src/arch/mod.rs index 25cfd990..49218407 100644 --- a/src/kernel/src/arch/mod.rs +++ b/src/kernel/src/arch/mod.rs @@ -51,8 +51,8 @@ impl MachDep { // kernel. let v = unsafe { std::ptr::read_unaligned(parms as _) }; - pcb.set_fsbase(v); - *pcb.flags_mut() |= PcbFlags::PCB_FULL_IRET; + pcb.fsbase = v; + pcb.flags |= PcbFlags::PCB_FULL_IRET; info!("FS segment has been changed to {v:#x}."); } diff --git a/src/kernel/src/ee/native/mod.rs b/src/kernel/src/ee/native/mod.rs index dea566fe..c48d30e8 100644 --- a/src/kernel/src/ee/native/mod.rs +++ b/src/kernel/src/ee/native/mod.rs @@ -590,7 +590,7 @@ impl NativeEngine { /// # Safety /// This method cannot be called from Rust. unsafe extern "sysv64" fn resolve_fs(&self, disp: usize) -> usize { - std::ptr::read_unaligned((VThread::current().unwrap().pcb().fsbase() + disp) as _) + std::ptr::read_unaligned((VThread::current().unwrap().pcb().fsbase + disp) as _) } /// # Safety diff --git a/src/kernel/src/event/ty.rs b/src/kernel/src/event/ty.rs index d16fb8ed..dbde63ea 100644 --- a/src/kernel/src/event/ty.rs +++ b/src/kernel/src/event/ty.rs @@ -22,6 +22,19 @@ impl EventType for fn(A) { } } +#[allow(coherence_leak_check)] // https://github.com/rust-lang/rust/issues/56105#issuecomment-606379619 +impl EventType for for<'a> fn(&'a A) { + type Handler = fn(&Arc, &A); + type Wrapper = Box; + + fn wrap_handler(s: Arc, h: Self::Handler) -> Self::Wrapper + where + S: Send + Sync + 'static, + { + Box::new(move |arg| h(&s, arg)) + } +} + #[allow(coherence_leak_check)] // https://github.com/rust-lang/rust/issues/56105#issuecomment-606379619 impl EventType for for<'a> fn(&'a mut A) { type Handler = fn(&Arc, &mut A); diff --git a/src/kernel/src/main.rs b/src/kernel/src/main.rs index bd9c50f2..31ec971b 100644 --- a/src/kernel/src/main.rs +++ b/src/kernel/src/main.rs @@ -425,7 +425,7 @@ fn run(args: Args) -> Result<(), KernelError> { let sys = Arc::new(sys); let budget_id = budget.create(Budget::new("big app", ProcType::BigApp)); let proc_root = fs.lookup(proc_root, true, None).unwrap(); - let proc = pmgr + let main = pmgr .spawn( ProcAbi::new(Some(sys)), auth, @@ -437,6 +437,7 @@ fn run(args: Args) -> Result<(), KernelError> { true, // TODO: Change to false when we switched to run /mini-syscore.elf. ) .map_err(KernelError::CreateProcessFailed)?; + let proc = main.proc(); info!( "Application stack: {:p}:{:p}", @@ -446,7 +447,6 @@ fn run(args: Args) -> Result<(), KernelError> { // Load eboot.bin. let path = vpath!("/app0/eboot.bin"); - let main = VThread::new(proc.clone()); let app = ld .exec(path, &main) .map_err(|e| KernelError::ExecFailed(path, e))?; @@ -527,14 +527,14 @@ fn run(args: Args) -> Result<(), KernelError> { std::thread::scope(|s| { let (tx, rx) = channel(); - for _ in 0..Hypervisor::VCPU { + for id in 0..Hypervisor::VCPU { let tx = tx.clone(); let exit = &exit; let hv = hv.as_ref(); let sched = sched.as_ref(); let pmgr = pmgr.as_ref(); - s.spawn(move || tx.send(cpu(exit, hv, sched, pmgr))); + s.spawn(move || tx.send(cpu(id, exit, hv, sched, pmgr))); } drop(tx); @@ -554,6 +554,7 @@ fn run(args: Args) -> Result<(), KernelError> { } fn cpu( + id: usize, exit: &AtomicBool, hv: &Hypervisor, sched: &Scheduler, @@ -563,7 +564,10 @@ fn cpu( let cpu = hv .create_cpu() .map_err(|e| CpuError::CreateCpuFailed(Box::new(e)))?; - let idle = Arc::new(VThread::new(pmgr.proc0().clone())); // TODO: Use a proper implementation. + let idle = pmgr.spawn_kthread( + Some(pmgr.idle()), + format!("SceIdleCpu{}", 7u8.wrapping_sub(id as u8)), + ); let mut cx = Pcpu::new(cpu.id(), Arc::downgrade(&idle)); // Enter dispatch loop. diff --git a/src/kernel/src/process/mod.rs b/src/kernel/src/process/mod.rs index f05eb756..81a65460 100644 --- a/src/kernel/src/process/mod.rs +++ b/src/kernel/src/process/mod.rs @@ -20,7 +20,7 @@ use std::collections::HashMap; use std::ffi::{c_char, c_int}; use std::mem::{size_of, transmute, zeroed}; use std::num::NonZeroI32; -use std::sync::atomic::AtomicI32; +use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::{Arc, RwLockWriteGuard, Weak}; use thiserror::Error; @@ -30,6 +30,7 @@ pub use self::binary::*; pub use self::cpuset::*; pub use self::filedesc::*; pub use self::group::*; +pub use self::pcb::*; pub use self::pid::*; pub use self::proc::*; pub use self::rlimit::*; @@ -44,6 +45,7 @@ mod binary; mod cpuset; mod filedesc; mod group; +mod pcb; mod pid; mod proc; mod rlimit; @@ -56,7 +58,9 @@ mod zombie; pub struct ProcManager { fs: Arc, rc: Arc, - proc0: Arc, // proc0 + proc0: Arc, // proc0 + thread0: Arc, // thread0 + idle: Arc, procs: Gutex>>, // allproc + pidhashtbl + zombproc sessions: Gutex>>, groups: Gutex>>, // pgrphashtbl @@ -75,28 +79,49 @@ impl ProcManager { sys: &mut Syscalls, ) -> Result, ProcManagerError> { // Setup proc0. + let root = fs.root(); let events = Arc::default(); let proc0 = VProc::new( Pid::KERNEL, + "kernel", kern.clone(), ProcAbi::new(None), None, None, DmemContainer::Zero, - fs.root(), + root.clone(), "", &events, ) .map_err(ProcManagerError::CreateProc0Failed)?; + // Setup thread0. + let thread0 = VThread::new(&proc0, NonZeroI32::new(Self::PID_MAX + 1).unwrap(), &events); + + proc0.threads_mut().push(thread0.clone()); + + // Create idle process. + let idle = VProc::new( + Pid::IDLE, + "idle", + kern.clone(), + ProcAbi::new(None), + None, + None, + DmemContainer::Zero, + root.clone(), + "", + &events, + ) + .map_err(ProcManagerError::CreateIdleFailed)?; + // Setup process list. - let proc0 = Arc::new(proc0); let mut list = HashMap::new(); let last_pid = 0; - let id = proc0.id(); - assert_eq!(id, last_pid); - assert!(list.insert(id, Arc::downgrade(&proc0)).is_none()); + assert_eq!(proc0.id(), last_pid); + assert!(list.insert(proc0.id(), Arc::downgrade(&proc0)).is_none()); + assert!(list.insert(idle.id(), Arc::downgrade(&idle)).is_none()); // Register syscalls. let gg = GutexGroup::new(); @@ -104,6 +129,8 @@ impl ProcManager { fs: fs.clone(), rc: rc.clone(), proc0, + thread0, + idle, procs: gg.spawn(list), sessions: gg.spawn(HashMap::new()), groups: gg.spawn(HashMap::new()), @@ -134,6 +161,14 @@ impl ProcManager { &self.proc0 } + pub fn thread0(&self) -> &Arc { + &self.thread0 + } + + pub fn idle(&self) -> &Arc { + &self.idle + } + pub fn events(&self) -> RwLockWriteGuard { self.events.lock() } @@ -149,7 +184,7 @@ impl ProcManager { root: Arc, system_path: impl Into, kernel: bool, - ) -> Result, SpawnError> { + ) -> Result, SpawnError> { use std::collections::hash_map::Entry; // Get credential. @@ -165,6 +200,7 @@ impl ProcManager { let pid = self.alloc_pid(kernel); let proc = VProc::new( pid, + "", // TODO: Copy from parent process. Arc::new(cred), abi, Some(budget_id), @@ -175,14 +211,22 @@ impl ProcManager { &self.events, )?; + // Create main thread. + let td = VThread::new( + &proc, + NonZeroI32::new(NEXT_TID.fetch_add(1, Ordering::Relaxed)).unwrap(), + &self.events, + ); + + proc.threads_mut().push(td.clone()); + // Add to list. - let proc = Arc::new(proc); let weak = Arc::downgrade(&proc); let mut list = self.procs.write(); match list.entry(proc.id()) { Entry::Occupied(mut e) => { - assert!(e.insert(weak).upgrade().is_none()); + assert_eq!(e.insert(weak).strong_count(), 0); } Entry::Vacant(e) => { e.insert(weak); @@ -191,7 +235,26 @@ impl ProcManager { drop(list); - Ok(proc) + Ok(td) + } + + /// See `kthread_add` on the PS4 for a reference. + pub fn spawn_kthread( + &self, + proc: Option<&Arc>, + name: impl Into, + ) -> Arc { + let proc = proc.unwrap_or(&self.proc0); + let td = VThread::new( + proc, + NonZeroI32::new(NEXT_TID.fetch_add(1, Ordering::Relaxed)).unwrap(), + &self.events, + ); + + proc.threads_mut().push(td.clone()); + + // TODO: Implement remaining logics. + td } fn sys_getpid(self: &Arc, td: &Arc, _: &SysIn) -> Result { @@ -403,11 +466,11 @@ impl ProcManager { fn sys_thr_set_name(self: &Arc, td: &Arc, i: &SysIn) -> Result { let tid: i64 = i.args[0].into(); - let name: Option<&str> = unsafe { i.args[1].to_str(32) }?; + let name = unsafe { i.args[1].to_str(32)?.unwrap_or("") }; let proc = td.proc(); if tid == -1 { - info!("Setting process name to '{}'.", name.unwrap_or("NULL")); + info!("Setting process name to '{name}'."); proc.set_name(name); } else { @@ -417,13 +480,9 @@ impl ProcManager { .find(|t| t.id().get() == tid as i32) .ok_or(SysErr::Raw(ESRCH))?; - info!( - "Setting name of thread {} to '{}'.", - thr.id(), - name.unwrap_or("NULL") - ); + info!("Setting name of thread {} to '{}'.", thr.id(), name); - thr.set_name(name); + thr.set_name(Some(name)); } Ok(SysOut::ZERO) @@ -795,7 +854,10 @@ impl ProcManager { /// Events that related to a process. #[derive(Default)] pub struct ProcEvents { - pub init: Event, // process_init + pub process_init: Event, + pub process_ctor: Event)>, + pub thread_init: Event, + pub thread_ctor: Event)>, } /// Implementation of `thr_param` structure. @@ -882,6 +944,9 @@ bitflags! { pub enum ProcManagerError { #[error("couldn't create proc0")] CreateProc0Failed(#[source] SpawnError), + + #[error("couldn't create idle proc")] + CreateIdleFailed(#[source] SpawnError), } /// Represents an error when [`ProcManager::spawn()`] fails. @@ -894,4 +959,5 @@ pub enum SpawnError { VmInitFailed(#[from] MemoryManagerError), } -static NEXT_ID: AtomicI32 = AtomicI32::new(123); +// TODO: Use a proper implementation. +static NEXT_TID: AtomicI32 = AtomicI32::new(ProcManager::PID_MAX + 2); diff --git a/src/kernel/src/process/pcb.rs b/src/kernel/src/process/pcb.rs new file mode 100644 index 00000000..29ed1177 --- /dev/null +++ b/src/kernel/src/process/pcb.rs @@ -0,0 +1,24 @@ +use bitflags::bitflags; + +/// Implementation of `pcb` structure. +#[derive(Default)] +pub struct Pcb { + pub r15: usize, // pcb_r15 + pub r14: usize, // pcb_r14 + pub r13: usize, // pcb_r13 + pub r12: usize, // pcb_r12 + pub rbp: usize, // pcb_rbp + pub rsp: usize, // pcb_rsp + pub rbx: usize, // pcb_rbx + pub rip: usize, // pcb_rip + pub fsbase: usize, // pcb_fsbase + pub flags: PcbFlags, // pcb_flags +} + +bitflags! { + /// Flags of [`Pcb`]. + #[derive(Default)] + pub struct PcbFlags: u32 { + const PCB_FULL_IRET = 0x01; + } +} diff --git a/src/kernel/src/process/pid.rs b/src/kernel/src/process/pid.rs index 028f7d1f..524ba0e6 100644 --- a/src/kernel/src/process/pid.rs +++ b/src/kernel/src/process/pid.rs @@ -8,6 +8,7 @@ pub struct Pid(i32); impl Pid { pub const KERNEL: Self = Self(0); + pub const IDLE: Self = Self(10); /// Returns [`None`] if `v` is negative. pub const fn new(v: i32) -> Option { diff --git a/src/kernel/src/process/proc.rs b/src/kernel/src/process/proc.rs index 10195ad9..e213dd00 100644 --- a/src/kernel/src/process/proc.rs +++ b/src/kernel/src/process/proc.rs @@ -17,9 +17,9 @@ use std::sync::atomic::AtomicPtr; use std::sync::Arc; /// An implementation of `proc` structure. -#[derive(Debug)] pub struct VProc { id: Pid, // p_pid + name: Gutex, // p_comm state: Gutex, // p_state threads: Gutex>>, // p_threads cred: Arc, // p_ucred @@ -30,7 +30,6 @@ pub struct VProc { files: Arc, // p_fd system_path: String, // p_randomized_path limits: Limits, // p_limit - comm: Gutex>, // p_comm bin: Gutex>, // p_dynlib? objects: Gutex>>, budget_id: Option, @@ -42,8 +41,9 @@ pub struct VProc { } impl VProc { - pub fn new( + pub(super) fn new( id: Pid, + name: impl Into, cred: Arc, abi: ProcAbi, budget_id: Option, @@ -52,12 +52,13 @@ impl VProc { root: Arc, system_path: impl Into, events: &Arc>, - ) -> Result { + ) -> Result, SpawnError> { let gg = GutexGroup::new(); let limits = Limits::load()?; let vm_space = VmSpace::new()?; - let mut vp = Self { + let mut proc = Self { id, + name: gg.spawn(name.into()), state: gg.spawn(ProcState::Active(ActiveProc::new())), threads: gg.spawn(Vec::new()), cred, @@ -72,18 +73,30 @@ impl VProc { budget_ptype, dmem_container: gg.spawn(dmem_container), limits, - comm: gg.spawn(None), //TODO: Find out how this is actually set bin: gg.spawn(None), app_info: AppInfo::new(), ptc: 0, uptc: AtomicPtr::new(null_mut()), }; - for h in events.trigger().select(|s| &s.init) { - h(&mut vp); + // Trigger process_init event. + let mut et = events.trigger(); + + for h in et.select(|s| &s.process_init) { + h(&mut proc); } - Ok(vp) + // Trigger process_ctor event. + let proc = Arc::new(proc); + let weak = Arc::downgrade(&proc); + + for h in et.select(|s| &s.process_ctor) { + h(&weak); + } + + drop(et); + + Ok(proc) } pub fn id(&self) -> Pid { @@ -130,8 +143,8 @@ impl VProc { &self.limits[ty] } - pub fn set_name(&self, name: Option<&str>) { - *self.comm.write() = name.map(|n| n.to_owned()); + pub fn set_name(&self, name: impl Into) { + *self.name.write() = name.into(); } pub fn bin(&self) -> GutexReadGuard> { diff --git a/src/kernel/src/process/thread.rs b/src/kernel/src/process/thread.rs index 14ad193a..52e288d3 100644 --- a/src/kernel/src/process/thread.rs +++ b/src/kernel/src/process/thread.rs @@ -1,22 +1,18 @@ -use super::{CpuMask, CpuSet, VProc, NEXT_ID}; +use super::{CpuMask, CpuSet, Pcb, ProcEvents, VProc}; use crate::errno::Errno; +use crate::event::EventSet; use crate::fs::VFile; use crate::signal::SignalSet; use crate::ucred::{CanSeeError, Privilege, PrivilegeError, Ucred}; -use bitflags::bitflags; use gmtx::{Gutex, GutexGroup, GutexReadGuard, GutexWriteGuard}; use llt::{OsThread, SpawnError}; use macros::Errno; use std::num::NonZeroI32; -use std::sync::atomic::Ordering; use std::sync::Arc; use thiserror::Error; use tls::{Local, Tls}; -/// An implementation of `thread` structure for the main application. -/// -/// See [`super::VProc`] for more information. -#[derive(Debug)] +/// An implementation of `thread` structure. pub struct VThread { proc: Arc, // td_proc id: NonZeroI32, // td_tid @@ -31,26 +27,44 @@ pub struct VThread { } impl VThread { - pub fn new(proc: Arc) -> Self { - // TODO: Check how the PS4 actually allocate the thread ID. + pub(super) fn new( + proc: &Arc, + id: NonZeroI32, + events: &Arc>, + ) -> Arc { let gg = GutexGroup::new(); let cred = proc.cred().clone(); - - Self { - proc, - id: NonZeroI32::new(NEXT_ID.fetch_add(1, Ordering::Relaxed)).unwrap(), + let mut td = Self { + proc: proc.clone(), + id, cred, sigmask: gg.spawn(SignalSet::default()), pri_class: 3, // TODO: Check the actual value on the PS4 when a thread is created. base_user_pri: 700, // TODO: Same here. - pcb: gg.spawn(Pcb { - fsbase: 0, - flags: PcbFlags::empty(), - }), + pcb: gg.spawn(Pcb::default()), cpuset: CpuSet::new(CpuMask::default()), // TODO: Same here. name: gg.spawn(None), // TODO: Same here fpop: gg.spawn(None), + }; + + // Trigger thread_init event. + let mut et = events.trigger(); + + for h in et.select(|s| &s.thread_init) { + h(&mut td); } + + // Trigger thread_ctor event. + let td = Arc::new(td); + let weak = Arc::downgrade(&td); + + for h in et.select(|s| &s.thread_ctor) { + h(&weak); + } + + drop(et); + + td } /// Return [`None`] if the calling thread is not a PS4 thread. @@ -133,7 +147,7 @@ impl VThread { /// The range of memory specified by `stack` and `stack_size` must be valid throughout lifetime /// of the thread. Specify an unaligned stack will cause undefined behavior. pub unsafe fn start( - self, + self: &Arc, stack: *mut u8, stack_size: usize, mut routine: F, @@ -141,12 +155,7 @@ impl VThread { where F: FnMut() + Send + 'static, { - let proc = self.proc.clone(); - let td = Arc::new(self); - let running = Running(td.clone()); - // Lock the list before spawn the thread to prevent race condition if the new thread run - // too fast and found out they is not in our list. - let mut threads = proc.threads_mut(); + let running = Running(self.clone()); let raw = llt::spawn(stack, stack_size, move || { // This closure must not have any variables that need to be dropped on the stack. The // reason is because this thread will be exited without returning from the routine. That @@ -155,42 +164,10 @@ impl VThread { routine(); })?; - // Add to the list. - threads.push(td); - Ok(raw) } } -/// An implementation of `pcb` structure. -#[derive(Debug)] -pub struct Pcb { - fsbase: usize, // pcb_fsbase - flags: PcbFlags, // pcb_flags -} - -impl Pcb { - pub fn fsbase(&self) -> usize { - self.fsbase - } - - pub fn set_fsbase(&mut self, v: usize) { - self.fsbase = v; - } - - pub fn flags_mut(&mut self) -> &mut PcbFlags { - &mut self.flags - } -} - -bitflags! { - /// Flags of [`Pcb`]. - #[derive(Debug)] - pub struct PcbFlags: u32 { - const PCB_FULL_IRET = 0x01; - } -} - // An object for removing the thread from the list when dropped. struct Running(Arc);