mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-06-11 02:16:01 -04:00
363 lines
11 KiB
Rust
363 lines
11 KiB
Rust
use crate::drivers::timer::Instant;
|
|
use crate::sched::CPU_STAT;
|
|
use crate::sched::sched_task::Work;
|
|
use crate::{
|
|
arch::ArchImpl,
|
|
kernel::cpu_id::CpuId,
|
|
memory::{
|
|
PAGE_ALLOC,
|
|
fault::{FaultResolution, handle_demand_fault},
|
|
},
|
|
sync::SpinLock,
|
|
};
|
|
use alloc::{
|
|
boxed::Box,
|
|
collections::btree_map::BTreeMap,
|
|
sync::{Arc, Weak},
|
|
};
|
|
use core::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
|
|
use core::time::Duration;
|
|
use creds::Credentials;
|
|
use fd_table::FileDescriptorTable;
|
|
use libkernel::memory::proc_vm::address_space::{UserAddressSpace, VirtualMemory};
|
|
use libkernel::{
|
|
error::{KernelError, Result},
|
|
fs::{Inode, pathbuf::PathBuf},
|
|
memory::{
|
|
address::{UA, VA},
|
|
allocators::phys::PageAllocation,
|
|
proc_vm::{ProcessVM, vmarea::AccessKind},
|
|
},
|
|
sync::waker_set::WakerSet,
|
|
};
|
|
use ptrace::PTrace;
|
|
use thread_group::pid::PidT;
|
|
use thread_group::signal::{AtomicSigSet, SigId};
|
|
use thread_group::{Tgid, ThreadGroup};
|
|
|
|
pub mod caps;
|
|
pub mod clone;
|
|
pub mod creds;
|
|
pub mod ctx;
|
|
pub mod epoll;
|
|
pub mod exec;
|
|
pub mod exit;
|
|
pub mod fd_table;
|
|
pub mod owned;
|
|
pub mod pidfd;
|
|
pub mod prctl;
|
|
pub mod ptrace;
|
|
pub mod sleep;
|
|
pub mod thread_group;
|
|
pub mod threading;
|
|
|
|
// the idle process (0) and the init process (1) are allocated manually.
|
|
static NEXT_TID: AtomicU32 = AtomicU32::new(2);
|
|
|
|
// Thread Id.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct Tid(pub u32);
|
|
|
|
impl Tid {
|
|
pub fn value(self) -> u32 {
|
|
self.0
|
|
}
|
|
|
|
pub fn from_tgid(tgid: Tgid) -> Self {
|
|
Self(tgid.0)
|
|
}
|
|
|
|
fn idle_for_cpu() -> Tid {
|
|
Self(CpuId::this().value() as _)
|
|
}
|
|
|
|
pub fn next_tid() -> Self {
|
|
Self(NEXT_TID.fetch_add(1, Ordering::Relaxed))
|
|
}
|
|
|
|
pub fn from_pid_t(pid: PidT) -> Self {
|
|
Self(pid as _)
|
|
}
|
|
}
|
|
|
|
/// A unqiue identifier for any task in the current system.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct TaskDescriptor {
|
|
tid: Tid,
|
|
tgid: Tgid,
|
|
}
|
|
|
|
impl TaskDescriptor {
|
|
pub fn from_tgid_tid(tgid: Tgid, tid: Tid) -> Self {
|
|
Self { tid, tgid }
|
|
}
|
|
|
|
/// Returns a descriptor for the idle task.
|
|
pub fn this_cpus_idle() -> Self {
|
|
Self {
|
|
tgid: Tgid(0),
|
|
tid: Tid(CpuId::this().value() as _),
|
|
}
|
|
}
|
|
|
|
/// Returns a representation of a descriptor encoded in a single pointer
|
|
/// value.
|
|
#[cfg(target_pointer_width = "64")]
|
|
pub fn to_ptr(self) -> *const () {
|
|
let mut value: u64 = self.tgid.value() as _;
|
|
|
|
value |= (self.tid.value() as u64) << 32;
|
|
|
|
value as _
|
|
}
|
|
|
|
/// Returns a descriptor decoded from a single pointer value. This is the
|
|
/// inverse of `to_ptr`.
|
|
#[cfg(target_pointer_width = "64")]
|
|
pub fn from_ptr(ptr: *const ()) -> Self {
|
|
let value = ptr as u64;
|
|
|
|
let tgid = value & 0xffffffff;
|
|
let tid = value >> 32;
|
|
|
|
Self {
|
|
tgid: Tgid(tgid as _),
|
|
tid: Tid(tid as _),
|
|
}
|
|
}
|
|
|
|
pub fn is_idle(&self) -> bool {
|
|
self.tgid.is_idle()
|
|
}
|
|
|
|
/// Returns the task-group ID (i.e. the PID) associated with this descriptor.
|
|
pub fn tgid(&self) -> Tgid {
|
|
self.tgid
|
|
}
|
|
|
|
/// Returns the thread ID associated with this descriptor.
|
|
pub fn tid(&self) -> Tid {
|
|
self.tid
|
|
}
|
|
}
|
|
|
|
pub type ProcVM = ProcessVM<<ArchImpl as VirtualMemory>::ProcessAddressSpace>;
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub struct Comm([u8; 16]);
|
|
|
|
impl Comm {
|
|
/// Create a new command name from the given string.
|
|
/// Truncates to 15 characters if necessary, and null-terminates.
|
|
pub fn new(name: &str) -> Self {
|
|
let mut comm = [0u8; 16];
|
|
let bytes = name.as_bytes();
|
|
let len = core::cmp::min(bytes.len(), 15);
|
|
comm[..len].copy_from_slice(&bytes[..len]);
|
|
Self(comm)
|
|
}
|
|
|
|
pub fn as_str(&self) -> &str {
|
|
let len = self.0.iter().position(|&c| c == 0).unwrap_or(16);
|
|
core::str::from_utf8(&self.0[..len]).unwrap_or("")
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct ITimer {
|
|
/// If interval is `None`, this timer is a one-shot timer.
|
|
pub interval: Option<Duration>,
|
|
/// Instant (wrt the needed clock) at which this timer will next expire.
|
|
pub next: Instant,
|
|
}
|
|
|
|
#[derive(Copy, Clone, Default)]
|
|
pub struct ITimers {
|
|
pub real: Option<ITimer>,
|
|
// virtual is a reserved keyword
|
|
pub virtual_: Option<ITimer>,
|
|
pub prof: Option<ITimer>,
|
|
}
|
|
|
|
pub struct Task {
|
|
pub tid: Tid,
|
|
pub comm: Arc<SpinLock<Comm>>,
|
|
pub process: Arc<ThreadGroup>,
|
|
pub vm: Arc<SpinLock<ProcVM>>,
|
|
pub cwd: Arc<SpinLock<(Arc<dyn Inode>, PathBuf)>>,
|
|
pub root: Arc<SpinLock<(Arc<dyn Inode>, PathBuf)>>,
|
|
pub creds: SpinLock<Credentials>,
|
|
pub i_timers: SpinLock<ITimers>,
|
|
pub fd_table: Arc<SpinLock<FileDescriptorTable>>,
|
|
pub ptrace: SpinLock<PTrace>,
|
|
pub sig_mask: AtomicSigSet,
|
|
pub pending_signals: AtomicSigSet,
|
|
pub signal_notifier: SpinLock<WakerSet>,
|
|
pub utime: AtomicUsize,
|
|
pub stime: AtomicUsize,
|
|
pub last_account: AtomicUsize,
|
|
}
|
|
|
|
impl Task {
|
|
pub fn is_idle_task(&self) -> bool {
|
|
self.process.tgid.is_idle()
|
|
}
|
|
|
|
pub fn pgid(&self) -> Tgid {
|
|
self.process.tgid
|
|
}
|
|
|
|
pub fn tid(&self) -> Tid {
|
|
self.tid
|
|
}
|
|
|
|
/// Raise a signal on this specific task (thread-directed).
|
|
pub fn raise_task_signal(&self, signal: SigId) {
|
|
self.pending_signals.insert(signal.into());
|
|
self.notify_signal_waiters();
|
|
}
|
|
|
|
pub fn notify_signal_waiters(&self) {
|
|
self.signal_notifier.lock_save_irq().wake_all();
|
|
}
|
|
|
|
/// Check for a pending signal on this task or its process, respecting the
|
|
/// signal mask.
|
|
pub fn peek_signal(&self) -> Option<SigId> {
|
|
let mask = self.sig_mask.load();
|
|
self.pending_signals.peek_signal(mask).or_else(|| {
|
|
self.process
|
|
.pending_signals
|
|
.lock_save_irq()
|
|
.peek_signal(mask)
|
|
})
|
|
}
|
|
|
|
/// Take a pending signal from this task or its process, respecting the
|
|
/// signal mask.
|
|
pub fn take_signal(&self) -> Option<SigId> {
|
|
let mask = self.sig_mask.load();
|
|
self.pending_signals.take_signal(mask).or_else(|| {
|
|
self.process
|
|
.pending_signals
|
|
.lock_save_irq()
|
|
.take_signal(mask)
|
|
})
|
|
}
|
|
|
|
/// Return a new descriptor that uniquely represents this task in the
|
|
/// system.
|
|
pub fn descriptor(&self) -> TaskDescriptor {
|
|
TaskDescriptor::from_tgid_tid(self.process.tgid, self.tid)
|
|
}
|
|
|
|
/// Get a page from the task's address space, in an atomic fashion - i.e.
|
|
/// with the process address space locked.
|
|
///
|
|
/// Handle any faults such that the page will be resident in memory and return
|
|
/// an incremented refcount for the page such that it will not be free'd until
|
|
/// the returned allocation handle is dropped.
|
|
///
|
|
/// SAFETY: The caller *must* guarantee that the returned page will only be
|
|
/// used as described in `access_kind`. i.e. if `AccessKind::Read` is passed
|
|
/// but data is written to this page, *bad* things will happen.
|
|
pub async unsafe fn get_page(
|
|
&self,
|
|
va: UA,
|
|
access_kind: AccessKind,
|
|
) -> Result<PageAllocation<'static, ArchImpl>> {
|
|
let va = VA::from_value(va.value());
|
|
|
|
let mut fut = None;
|
|
|
|
loop {
|
|
if let Some(fut) = fut.take() {
|
|
// Handle async fault.
|
|
Box::into_pin(fut).await?;
|
|
}
|
|
|
|
{
|
|
let mut vm = self.vm.lock_save_irq();
|
|
|
|
if let Some(pa) = vm.mm_mut().address_space_mut().translate(va) {
|
|
let region = pa.pfn.as_phys_range();
|
|
|
|
if match access_kind {
|
|
AccessKind::Read => pa.perms.is_read(),
|
|
AccessKind::Write => pa.perms.is_write(),
|
|
AccessKind::Execute => pa.perms.is_execute(),
|
|
} {
|
|
let alloc = unsafe { PAGE_ALLOC.get().unwrap().alloc_from_region(region) };
|
|
// Increase refcount on this page, ensuring it isn't reused
|
|
// while we copy the data.
|
|
let ret = alloc.clone();
|
|
|
|
// The original allocation is still owned by the address
|
|
// space.
|
|
alloc.leak();
|
|
|
|
return Ok(ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try to handle the fault.
|
|
match handle_demand_fault(self.vm.clone(), va, access_kind)? {
|
|
// Resolved the fault. Try again
|
|
FaultResolution::Resolved => continue,
|
|
FaultResolution::Denied => return Err(KernelError::Fault),
|
|
FaultResolution::Deferred(future) => {
|
|
fut = Some(future);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn update_utime(&self, now: Instant) {
|
|
let now = now.user_normalized();
|
|
let now = now.ticks() as usize;
|
|
let last_account = self.last_account.load(Ordering::Relaxed);
|
|
let delta = now.saturating_sub(last_account);
|
|
if self.is_idle_task() {
|
|
CPU_STAT.get().idle.fetch_add(delta, Ordering::Relaxed);
|
|
} else {
|
|
CPU_STAT.get().user.fetch_add(delta, Ordering::Relaxed);
|
|
}
|
|
self.utime.fetch_add(delta, Ordering::Relaxed);
|
|
self.process.utime.fetch_add(delta, Ordering::Relaxed);
|
|
self.last_account.store(now, Ordering::Relaxed);
|
|
}
|
|
|
|
pub fn update_stime(&self, now: Instant) {
|
|
let now = now.user_normalized();
|
|
let now = now.ticks() as usize;
|
|
let last_account = self.last_account.load(Ordering::Relaxed);
|
|
let delta = now.saturating_sub(last_account);
|
|
CPU_STAT.get().system.fetch_add(delta, Ordering::Relaxed);
|
|
self.stime.fetch_add(delta, Ordering::Relaxed);
|
|
self.process.stime.fetch_add(delta, Ordering::Relaxed);
|
|
self.last_account.store(now, Ordering::Relaxed);
|
|
}
|
|
|
|
pub fn reset_last_account(&self, now: Instant) {
|
|
let now = now.user_normalized();
|
|
let now = now.ticks() as usize;
|
|
self.last_account.store(now, Ordering::Relaxed);
|
|
self.last_account.store(now, Ordering::Relaxed);
|
|
}
|
|
}
|
|
|
|
/// Finds a task by it's `Tid`.
|
|
pub fn find_task_by_tid(tid: Tid) -> Option<Arc<Work>> {
|
|
TASK_LIST
|
|
.lock_save_irq()
|
|
.get(&tid)
|
|
.and_then(|x| x.upgrade())
|
|
}
|
|
|
|
pub static TASK_LIST: SpinLock<BTreeMap<Tid, Weak<Work>>> = SpinLock::new(BTreeMap::new());
|
|
|
|
unsafe impl Send for Task {}
|
|
unsafe impl Sync for Task {}
|