Implements idle process (#887)

This commit is contained in:
Putta Khunchalee 2024-07-14 00:21:01 +07:00 committed by GitHub
parent bf704b5311
commit 6ca06469d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 194 additions and 96 deletions

View File

@ -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}.");
}

View File

@ -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

View File

@ -22,6 +22,19 @@ impl<A: 'static> EventType for fn(A) {
}
}
#[allow(coherence_leak_check)] // https://github.com/rust-lang/rust/issues/56105#issuecomment-606379619
impl<A: 'static> EventType for for<'a> fn(&'a A) {
type Handler<S: 'static> = fn(&Arc<S>, &A);
type Wrapper = Box<dyn Fn(&A) + Send + Sync>;
fn wrap_handler<S>(s: Arc<S>, h: Self::Handler<S>) -> 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<A: 'static> EventType for for<'a> fn(&'a mut A) {
type Handler<S: 'static> = fn(&Arc<S>, &mut A);

View File

@ -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.

View File

@ -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<Fs>,
rc: Arc<RcMgr>,
proc0: Arc<VProc>, // proc0
proc0: Arc<VProc>, // proc0
thread0: Arc<VThread>, // thread0
idle: Arc<VProc>,
procs: Gutex<HashMap<Pid, Weak<VProc>>>, // allproc + pidhashtbl + zombproc
sessions: Gutex<HashMap<Pid, Weak<VSession>>>,
groups: Gutex<HashMap<Pid, Weak<VProcGroup>>>, // pgrphashtbl
@ -75,28 +79,49 @@ impl ProcManager {
sys: &mut Syscalls,
) -> Result<Arc<Self>, 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<VThread> {
&self.thread0
}
pub fn idle(&self) -> &Arc<VProc> {
&self.idle
}
pub fn events(&self) -> RwLockWriteGuard<ProcEvents> {
self.events.lock()
}
@ -149,7 +184,7 @@ impl ProcManager {
root: Arc<Vnode>,
system_path: impl Into<String>,
kernel: bool,
) -> Result<Arc<VProc>, SpawnError> {
) -> Result<Arc<VThread>, 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<VProc>>,
name: impl Into<String>,
) -> Arc<VThread> {
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<Self>, td: &Arc<VThread>, _: &SysIn) -> Result<SysOut, SysErr> {
@ -403,11 +466,11 @@ impl ProcManager {
fn sys_thr_set_name(self: &Arc<Self>, td: &Arc<VThread>, i: &SysIn) -> Result<SysOut, SysErr> {
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<fn(&mut VProc)>, // process_init
pub process_init: Event<fn(&mut VProc)>,
pub process_ctor: Event<fn(&Weak<VProc>)>,
pub thread_init: Event<fn(&mut VThread)>,
pub thread_ctor: Event<fn(&Weak<VThread>)>,
}
/// 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);

View File

@ -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;
}
}

View File

@ -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<Self> {

View File

@ -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<String>, // p_comm
state: Gutex<ProcState>, // p_state
threads: Gutex<Vec<Arc<VThread>>>, // p_threads
cred: Arc<Ucred>, // p_ucred
@ -30,7 +30,6 @@ pub struct VProc {
files: Arc<FileDesc>, // p_fd
system_path: String, // p_randomized_path
limits: Limits, // p_limit
comm: Gutex<Option<String>>, // p_comm
bin: Gutex<Option<Binaries>>, // p_dynlib?
objects: Gutex<Idt<Arc<dyn Any + Send + Sync>>>,
budget_id: Option<usize>,
@ -42,8 +41,9 @@ pub struct VProc {
}
impl VProc {
pub fn new(
pub(super) fn new(
id: Pid,
name: impl Into<String>,
cred: Arc<Ucred>,
abi: ProcAbi,
budget_id: Option<usize>,
@ -52,12 +52,13 @@ impl VProc {
root: Arc<Vnode>,
system_path: impl Into<String>,
events: &Arc<EventSet<ProcEvents>>,
) -> Result<Self, SpawnError> {
) -> Result<Arc<Self>, 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<String>) {
*self.name.write() = name.into();
}
pub fn bin(&self) -> GutexReadGuard<Option<Binaries>> {

View File

@ -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<VProc>, // td_proc
id: NonZeroI32, // td_tid
@ -31,26 +27,44 @@ pub struct VThread {
}
impl VThread {
pub fn new(proc: Arc<VProc>) -> Self {
// TODO: Check how the PS4 actually allocate the thread ID.
pub(super) fn new(
proc: &Arc<VProc>,
id: NonZeroI32,
events: &Arc<EventSet<ProcEvents>>,
) -> Arc<Self> {
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<F>(
self,
self: &Arc<Self>,
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<VThread>);