parent
fe4ee39b03
commit
cf54a7b958
@ -0,0 +1,2 @@
|
||||
target
|
||||
Cargo.lock
|
||||
@ -0,0 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"object",
|
||||
"kernel-hal-unix",
|
||||
"kernel-hal",
|
||||
]
|
||||
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "kernel-hal-unix"
|
||||
version = "0.1.0"
|
||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
||||
edition = "2018"
|
||||
description = "Kernel HAL implementation on Linux and macOS."
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
kernel-hal = { path = "../kernel-hal" }
|
||||
async-std = "1.9"
|
||||
trapframe = "0.8.0"
|
||||
@ -0,0 +1,46 @@
|
||||
#![feature(asm)]
|
||||
#![feature(linkage)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use {
|
||||
alloc::boxed::Box,
|
||||
core::time::Duration,
|
||||
core::{future::Future, pin::Pin},
|
||||
std::time::SystemTime,
|
||||
};
|
||||
|
||||
pub use trapframe::{GeneralRegs, UserContext};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Thread {
|
||||
thread: usize,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
#[export_name = "hal_thread_spawn"]
|
||||
pub fn spawn(
|
||||
future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
|
||||
_vmtoken: usize,
|
||||
) -> Self {
|
||||
async_std::task::spawn(future);
|
||||
Thread { thread: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// Get current time.
|
||||
#[export_name = "hal_timer_now"]
|
||||
pub fn timer_now() -> Duration {
|
||||
SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Initialize the HAL.
|
||||
///
|
||||
/// This function must be called at the beginning.
|
||||
pub fn init() {
|
||||
#[cfg(target_os = "macos")]
|
||||
unimplemented!()
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "kernel-hal"
|
||||
version = "0.1.0"
|
||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
||||
edition = "2018"
|
||||
description = "Kernel HAL interface definations."
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
trapframe = "0.8.0"
|
||||
@ -0,0 +1,35 @@
|
||||
#![no_std]
|
||||
#![feature(linkage)]
|
||||
#![deny(warnings)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub use trapframe::{GeneralRegs, UserContext};
|
||||
|
||||
use {
|
||||
alloc::boxed::Box,
|
||||
core::{future::Future, pin::Pin, time::Duration},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Thread {
|
||||
id: usize,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
/// Spawn a new thread.
|
||||
#[linkage = "weak"]
|
||||
#[export_name = "hal_thread_spawn"]
|
||||
pub fn spawn(
|
||||
_future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
|
||||
_vmtoken: usize,
|
||||
) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[linkage = "weak"]
|
||||
#[export_name = "hal_timer_now"]
|
||||
pub fn timer_now() -> Duration {
|
||||
unimplemented!()
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "ch02-03"
|
||||
version = "0.1.0"
|
||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
spin = "0.7"
|
||||
downcast-rs = { version = "1.2.0", default-features = false }
|
||||
bitflags = "1.2"
|
||||
hashbrown = "0.9"
|
||||
trapframe = "0.8.0"
|
||||
futures = { version = "0.3", default-features = false, features = ["alloc", "async-await"] }
|
||||
async-std = { version = "1.9", features = ["attributes", "unstable"] }
|
||||
numeric-enum-macro = "0.2"
|
||||
kernel-hal = { path = "../kernel-hal" }
|
||||
kernel-hal-unix = { path = "../kernel-hal-unix" }
|
||||
@ -0,0 +1,232 @@
|
||||
// ANCHOR: result
|
||||
///
|
||||
pub type ZxResult<T = ()> = Result<T, ZxError>;
|
||||
// ANCHOR_END: result
|
||||
|
||||
// ANCHOR: error_begin
|
||||
/// Zircon statuses are signed 32 bit integers. The space of values is
|
||||
/// divided as follows:
|
||||
/// - The zero value is for the OK status.
|
||||
/// - Negative values are defined by the system, in this file.
|
||||
/// - Positive values are reserved for protocol-specific error values,
|
||||
/// and will never be defined by the system.
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum ZxError {
|
||||
OK = 0,
|
||||
|
||||
// ======= Internal failures =======
|
||||
/// The system encountered an otherwise unspecified error
|
||||
/// while performing the operation.
|
||||
INTERNAL = -1,
|
||||
|
||||
/// The operation is not implemented, supported,
|
||||
/// or enabled.
|
||||
NOT_SUPPORTED = -2,
|
||||
// ANCHOR_END: error_begin
|
||||
/// The system was not able to allocate some resource
|
||||
/// needed for the operation.
|
||||
NO_RESOURCES = -3,
|
||||
|
||||
/// The system was not able to allocate memory needed
|
||||
/// for the operation.
|
||||
NO_MEMORY = -4,
|
||||
|
||||
// -5 used to be ZX_ERR_CALL_FAILED.
|
||||
/// The system call was interrupted, but should be
|
||||
/// retried. This should not be seen outside of the VDSO.
|
||||
INTERNAL_INTR_RETRY = -6,
|
||||
|
||||
// ======= Parameter errors =======
|
||||
/// an argument is invalid, ex. null pointer
|
||||
INVALID_ARGS = -10,
|
||||
|
||||
/// A specified handle value does not refer to a handle.
|
||||
BAD_HANDLE = -11,
|
||||
|
||||
/// The subject of the operation is the wrong type to
|
||||
/// perform the operation.
|
||||
/// Example: Attempting a message_read on a thread handle.
|
||||
WRONG_TYPE = -12,
|
||||
|
||||
/// The specified syscall number is invalid.
|
||||
BAD_SYSCALL = -13,
|
||||
|
||||
/// An argument is outside the valid range for this
|
||||
/// operation.
|
||||
OUT_OF_RANGE = -14,
|
||||
|
||||
/// A caller provided buffer is too small for
|
||||
/// this operation.
|
||||
BUFFER_TOO_SMALL = -15,
|
||||
|
||||
// ======= Precondition or state errors =======
|
||||
/// operation failed because the current state of the
|
||||
/// object does not allow it, or a precondition of the operation is
|
||||
/// not satisfied
|
||||
BAD_STATE = -20,
|
||||
|
||||
/// The time limit for the operation elapsed before
|
||||
/// the operation completed.
|
||||
TIMED_OUT = -21,
|
||||
|
||||
/// The operation cannot be performed currently but
|
||||
/// potentially could succeed if the caller waits for a prerequisite
|
||||
/// to be satisfied, for example waiting for a handle to be readable
|
||||
/// or writable.
|
||||
/// Example: Attempting to read from a channel that has no
|
||||
/// messages waiting but has an open remote will return ZX_ERR_SHOULD_WAIT.
|
||||
/// Attempting to read from a channel that has no messages waiting
|
||||
/// and has a closed remote end will return ZX_ERR_PEER_CLOSED.
|
||||
SHOULD_WAIT = -22,
|
||||
|
||||
/// The in-progress operation (e.g. a wait) has been
|
||||
/// canceled.
|
||||
CANCELED = -23,
|
||||
|
||||
/// The operation failed because the remote end of the
|
||||
/// subject of the operation was closed.
|
||||
PEER_CLOSED = -24,
|
||||
|
||||
/// The requested entity is not found.
|
||||
NOT_FOUND = -25,
|
||||
|
||||
/// An object with the specified identifier
|
||||
/// already exists.
|
||||
/// Example: Attempting to create a file when a file already exists
|
||||
/// with that name.
|
||||
ALREADY_EXISTS = -26,
|
||||
|
||||
/// The operation failed because the named entity
|
||||
/// is already owned or controlled by another entity. The operation
|
||||
/// could succeed later if the current owner releases the entity.
|
||||
ALREADY_BOUND = -27,
|
||||
|
||||
/// The subject of the operation is currently unable
|
||||
/// to perform the operation.
|
||||
/// Note: This is used when there's no direct way for the caller to
|
||||
/// observe when the subject will be able to perform the operation
|
||||
/// and should thus retry.
|
||||
UNAVAILABLE = -28,
|
||||
|
||||
// ======= Permission check errors =======
|
||||
/// The caller did not have permission to perform
|
||||
/// the specified operation.
|
||||
ACCESS_DENIED = -30,
|
||||
|
||||
// ======= Input-output errors =======
|
||||
/// Otherwise unspecified error occurred during I/O.
|
||||
IO = -40,
|
||||
|
||||
/// The entity the I/O operation is being performed on
|
||||
/// rejected the operation.
|
||||
/// Example: an I2C device NAK'ing a transaction or a disk controller
|
||||
/// rejecting an invalid command, or a stalled USB endpoint.
|
||||
IO_REFUSED = -41,
|
||||
|
||||
/// The data in the operation failed an integrity
|
||||
/// check and is possibly corrupted.
|
||||
/// Example: CRC or Parity error.
|
||||
IO_DATA_INTEGRITY = -42,
|
||||
|
||||
/// The data in the operation is currently unavailable
|
||||
/// and may be permanently lost.
|
||||
/// Example: A disk block is irrecoverably damaged.
|
||||
IO_DATA_LOSS = -43,
|
||||
|
||||
/// The device is no longer available (has been
|
||||
/// unplugged from the system, powered down, or the driver has been
|
||||
/// unloaded,
|
||||
IO_NOT_PRESENT = -44,
|
||||
|
||||
/// More data was received from the device than expected.
|
||||
/// Example: a USB "babble" error due to a device sending more data than
|
||||
/// the host queued to receive.
|
||||
IO_OVERRUN = -45,
|
||||
|
||||
/// An operation did not complete within the required timeframe.
|
||||
/// Example: A USB isochronous transfer that failed to complete due to an overrun or underrun.
|
||||
IO_MISSED_DEADLINE = -46,
|
||||
|
||||
/// The data in the operation is invalid parameter or is out of range.
|
||||
/// Example: A USB transfer that failed to complete with TRB Error
|
||||
IO_INVALID = -47,
|
||||
|
||||
// ======== Filesystem Errors ========
|
||||
/// Path name is too long.
|
||||
BAD_PATH = -50,
|
||||
|
||||
/// Object is not a directory or does not support
|
||||
/// directory operations.
|
||||
/// Example: Attempted to open a file as a directory or
|
||||
/// attempted to do directory operations on a file.
|
||||
NOT_DIR = -51,
|
||||
|
||||
/// Object is not a regular file.
|
||||
NOT_FILE = -52,
|
||||
|
||||
/// This operation would cause a file to exceed a
|
||||
/// filesystem-specific size limit
|
||||
FILE_BIG = -53,
|
||||
|
||||
/// Filesystem or device space is exhausted.
|
||||
NO_SPACE = -54,
|
||||
|
||||
/// Directory is not empty.
|
||||
NOT_EMPTY = -55,
|
||||
|
||||
// ======== Flow Control ========
|
||||
// These are not errors, as such, and will never be returned
|
||||
// by a syscall or public API. They exist to allow callbacks
|
||||
// to request changes in operation.
|
||||
/// Do not call again.
|
||||
/// Example: A notification callback will be called on every
|
||||
/// event until it returns something other than ZX_OK.
|
||||
/// This status allows differentiation between "stop due to
|
||||
/// an error" and "stop because the work is done."
|
||||
STOP = -60,
|
||||
|
||||
/// Advance to the next item.
|
||||
/// Example: A notification callback will use this response
|
||||
/// to indicate it did not "consume" an item passed to it,
|
||||
/// but by choice, not due to an error condition.
|
||||
NEXT = -61,
|
||||
|
||||
/// Ownership of the item has moved to an asynchronous worker.
|
||||
///
|
||||
/// Unlike ZX_ERR_STOP, which implies that iteration on an object
|
||||
/// should stop, and ZX_ERR_NEXT, which implies that iteration
|
||||
/// should continue to the next item, ZX_ERR_ASYNC implies
|
||||
/// that an asynchronous worker is responsible for continuing iteration.
|
||||
///
|
||||
/// Example: A notification callback will be called on every
|
||||
/// event, but one event needs to handle some work asynchronously
|
||||
/// before it can continue. ZX_ERR_ASYNC implies the worker is
|
||||
/// responsible for resuming iteration once its work has completed.
|
||||
ASYNC = -62,
|
||||
|
||||
// ======== Network-related errors ========
|
||||
/// Specified protocol is not
|
||||
/// supported.
|
||||
PROTOCOL_NOT_SUPPORTED = -70,
|
||||
|
||||
/// Host is unreachable.
|
||||
ADDRESS_UNREACHABLE = -71,
|
||||
|
||||
/// Address is being used by someone else.
|
||||
ADDRESS_IN_USE = -72,
|
||||
|
||||
/// Socket is not connected.
|
||||
NOT_CONNECTED = -73,
|
||||
|
||||
/// Remote peer rejected the connection.
|
||||
CONNECTION_REFUSED = -74,
|
||||
|
||||
/// Connection was reset.
|
||||
CONNECTION_RESET = -75,
|
||||
// ANCHOR: error_end
|
||||
/// Connection was aborted.
|
||||
CONNECTION_ABORTED = -76,
|
||||
}
|
||||
// ANCHOR_END: error_end
|
||||
@ -0,0 +1,176 @@
|
||||
use {
|
||||
super::*,
|
||||
crate::error::*,
|
||||
crate::object::*,
|
||||
alloc::collections::VecDeque,
|
||||
alloc::sync::{Arc, Weak},
|
||||
alloc::vec::Vec,
|
||||
core::sync::atomic::{AtomicU32, Ordering},
|
||||
spin::Mutex,
|
||||
};
|
||||
|
||||
pub struct Channel {
|
||||
base: KObjectBase,
|
||||
peer: Weak<Channel>,
|
||||
recv_queue: Mutex<VecDeque<T>>,
|
||||
next_txid: AtomicU32,
|
||||
}
|
||||
|
||||
type T = MessagePacket;
|
||||
type TxID = u32;
|
||||
|
||||
impl_kobject!(Channel
|
||||
fn peer(&self) -> ZxResult<Arc<dyn KernelObject>> {
|
||||
let peer = self.peer.upgrade().ok_or(ZxError::PEER_CLOSED)?;
|
||||
Ok(peer)
|
||||
}
|
||||
fn related_koid(&self) -> KoID {
|
||||
self.peer.upgrade().map(|p| p.id()).unwrap_or(0)
|
||||
}
|
||||
);
|
||||
|
||||
impl Channel {
|
||||
/// Create a channel and return a pair of its endpoints
|
||||
#[allow(unsafe_code)]
|
||||
pub fn create() -> (Arc<Self>, Arc<Self>) {
|
||||
let mut channel0 = Arc::new(Channel {
|
||||
base: KObjectBase::default(),
|
||||
peer: Weak::default(),
|
||||
recv_queue: Default::default(),
|
||||
next_txid: AtomicU32::new(0x8000_0000),
|
||||
});
|
||||
let channel1 = Arc::new(Channel {
|
||||
base: KObjectBase::default(),
|
||||
peer: Arc::downgrade(&channel0),
|
||||
recv_queue: Default::default(),
|
||||
next_txid: AtomicU32::new(0x8000_0000),
|
||||
});
|
||||
// no other reference of `channel0`
|
||||
unsafe {
|
||||
Arc::get_mut_unchecked(&mut channel0).peer = Arc::downgrade(&channel1);
|
||||
}
|
||||
(channel0, channel1)
|
||||
}
|
||||
|
||||
/// Read a packet from the channel if check is ok, otherwise the msg will keep.
|
||||
pub fn read(&self) -> ZxResult<T> {
|
||||
let mut recv_queue = self.recv_queue.lock();
|
||||
if let Some(_) = recv_queue.front() {
|
||||
let msg = recv_queue.pop_front().unwrap();
|
||||
return Ok(msg);
|
||||
}
|
||||
if self.peer_closed() {
|
||||
Err(ZxError::PEER_CLOSED)
|
||||
} else {
|
||||
Err(ZxError::SHOULD_WAIT)
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a packet to the channel
|
||||
pub fn write(&self, msg: T) -> ZxResult {
|
||||
let peer = self.peer.upgrade().ok_or(ZxError::PEER_CLOSED)?;
|
||||
peer.push_general(msg);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Push a message to general queue, called from peer.
|
||||
fn push_general(&self, msg: T) {
|
||||
let mut send_queue = self.recv_queue.lock();
|
||||
send_queue.push_back(msg);
|
||||
}
|
||||
|
||||
/// Generate a new transaction ID for `call`.
|
||||
fn new_txid(&self) -> TxID {
|
||||
self.next_txid.fetch_add(1, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
/// Is peer channel closed?
|
||||
fn peer_closed(&self) -> bool {
|
||||
self.peer.strong_count() == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// The message transferred in the channel.
|
||||
/// See [Channel](struct.Channel.html) for details.
|
||||
#[derive(Default)]
|
||||
pub struct MessagePacket {
|
||||
/// The transition id of the message packet
|
||||
pub txid: TxID,
|
||||
/// The data carried by the message packet
|
||||
pub data: Vec<u8>,
|
||||
/// See [Channel](struct.Channel.html) for details.
|
||||
pub handles: Vec<Handle>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_basics() {
|
||||
let (end0, end1) = Channel::create();
|
||||
assert!(Arc::ptr_eq(
|
||||
&end0.peer().unwrap().downcast_arc().unwrap(),
|
||||
&end1
|
||||
));
|
||||
assert_eq!(end0.related_koid(), end1.id());
|
||||
|
||||
drop(end1);
|
||||
assert_eq!(end0.peer().unwrap_err(), ZxError::PEER_CLOSED);
|
||||
assert_eq!(end0.related_koid(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_write() {
|
||||
let (channel0, channel1) = Channel::create();
|
||||
// write a message to each other
|
||||
let txid0 = channel0.new_txid();
|
||||
channel0
|
||||
.write(MessagePacket {
|
||||
txid: txid0,
|
||||
data: Vec::from("hello 1"),
|
||||
handles: Vec::new(),
|
||||
})
|
||||
.unwrap();
|
||||
let txid1 = channel1.new_txid();
|
||||
channel1
|
||||
.write(MessagePacket {
|
||||
txid: txid1,
|
||||
data: Vec::from("hello 0"),
|
||||
handles: Vec::new(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// read message should success
|
||||
let recv_msg = channel1.read().unwrap();
|
||||
assert_eq!(recv_msg.txid, txid0);
|
||||
assert_eq!(recv_msg.data.as_slice(), b"hello 1");
|
||||
assert!(recv_msg.handles.is_empty());
|
||||
|
||||
let recv_msg = channel0.read().unwrap();
|
||||
assert_eq!(recv_msg.txid, txid1);
|
||||
assert_eq!(recv_msg.data.as_slice(), b"hello 0");
|
||||
assert!(recv_msg.handles.is_empty());
|
||||
|
||||
// read more message should fail.
|
||||
assert_eq!(channel0.read().err(), Some(ZxError::SHOULD_WAIT));
|
||||
assert_eq!(channel1.read().err(), Some(ZxError::SHOULD_WAIT));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn peer_closed() {
|
||||
let (channel0, channel1) = Channel::create();
|
||||
// write a message from peer, then drop it
|
||||
channel1.write(MessagePacket::default()).unwrap();
|
||||
drop(channel1);
|
||||
// read the first message should success.
|
||||
channel0.read().unwrap();
|
||||
// read more message should fail.
|
||||
assert_eq!(channel0.read().err(), Some(ZxError::PEER_CLOSED));
|
||||
// write message should fail.
|
||||
assert_eq!(
|
||||
channel0.write(MessagePacket::default()),
|
||||
Err(ZxError::PEER_CLOSED)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
use super::*;
|
||||
|
||||
mod channel;
|
||||
pub use self::channel::*;
|
||||
@ -0,0 +1,18 @@
|
||||
#![no_std]
|
||||
#![deny(unused_imports)]
|
||||
#![allow(dead_code)]
|
||||
#![feature(get_mut_unchecked)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
mod error;
|
||||
mod ipc;
|
||||
mod object;
|
||||
mod task;
|
||||
mod vm;
|
||||
|
||||
pub use self::error::*;
|
||||
@ -0,0 +1,21 @@
|
||||
// ANCHOR: handle
|
||||
use super::{KernelObject, Rights};
|
||||
use alloc::sync::Arc;
|
||||
|
||||
pub type HandleValue = u32;
|
||||
pub const INVALID_HANDLE: HandleValue = 0;
|
||||
|
||||
/// 内核对象句柄
|
||||
#[derive(Clone)]
|
||||
pub struct Handle {
|
||||
pub object: Arc<dyn KernelObject>,
|
||||
pub rights: Rights,
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
/// 创建一个新句柄
|
||||
pub fn new(object: Arc<dyn KernelObject>, rights: Rights) -> Self {
|
||||
Handle { object, rights }
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: handle
|
||||
@ -0,0 +1,409 @@
|
||||
use {
|
||||
super::job_policy::*,
|
||||
super::process::Process,
|
||||
super::*,
|
||||
crate::error::*,
|
||||
crate::object::*,
|
||||
crate::task::Task,
|
||||
alloc::sync::{Arc, Weak},
|
||||
alloc::vec::Vec,
|
||||
spin::Mutex,
|
||||
};
|
||||
|
||||
/// Job 对象
|
||||
#[allow(dead_code)]
|
||||
pub struct Job {
|
||||
base: KObjectBase,
|
||||
parent: Option<Arc<Job>>,
|
||||
parent_policy: JobPolicy,
|
||||
inner: Mutex<JobInner>,
|
||||
}
|
||||
|
||||
impl_kobject!(Job
|
||||
fn get_child(&self, id: KoID) -> ZxResult<Arc<dyn KernelObject>> {
|
||||
let inner = self.inner.lock();
|
||||
if let Some(job) = inner.children.iter().filter_map(|o|o.upgrade()).find(|o| o.id() == id) {
|
||||
return Ok(job);
|
||||
}
|
||||
if let Some(proc) = inner.processes.iter().find(|o| o.id() == id) {
|
||||
return Ok(proc.clone());
|
||||
}
|
||||
Err(ZxError::NOT_FOUND)
|
||||
}
|
||||
fn related_koid(&self) -> KoID {
|
||||
self.parent.as_ref().map(|p| p.id()).unwrap_or(0)
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Default)]
|
||||
struct JobInner {
|
||||
policy: JobPolicy,
|
||||
children: Vec<Weak<Job>>,
|
||||
processes: Vec<Arc<Process>>,
|
||||
// if the job is killed, no more child creation should works
|
||||
killed: bool,
|
||||
self_ref: Weak<Job>,
|
||||
}
|
||||
|
||||
impl Job {
|
||||
/// Create the root job.
|
||||
pub fn root() -> Arc<Self> {
|
||||
let job = Arc::new(Job {
|
||||
base: KObjectBase::new(),
|
||||
parent: None,
|
||||
parent_policy: JobPolicy::default(),
|
||||
inner: Mutex::new(JobInner::default()),
|
||||
});
|
||||
job.inner.lock().self_ref = Arc::downgrade(&job);
|
||||
job
|
||||
}
|
||||
|
||||
/// Create a new child job object.
|
||||
pub fn create_child(self: &Arc<Self>) -> ZxResult<Arc<Self>> {
|
||||
let mut inner = self.inner.lock();
|
||||
if inner.killed {
|
||||
return Err(ZxError::BAD_STATE);
|
||||
}
|
||||
let child = Arc::new(Job {
|
||||
base: KObjectBase::new(),
|
||||
parent: Some(self.clone()),
|
||||
parent_policy: inner.policy.merge(&self.parent_policy),
|
||||
inner: Mutex::new(JobInner::default()),
|
||||
});
|
||||
let child_weak = Arc::downgrade(&child);
|
||||
child.inner.lock().self_ref = child_weak.clone();
|
||||
inner.children.push(child_weak);
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
fn remove_child(&self, to_remove: &Weak<Job>) {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.children.retain(|child| !to_remove.ptr_eq(child));
|
||||
if inner.killed && inner.processes.is_empty() && inner.children.is_empty() {
|
||||
drop(inner);
|
||||
self.terminate()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the policy of the job.
|
||||
pub fn policy(&self) -> JobPolicy {
|
||||
self.inner.lock().policy.merge(&self.parent_policy)
|
||||
}
|
||||
|
||||
/// Get the parent job.
|
||||
pub fn parent(&self) -> Option<Arc<Self>> {
|
||||
self.parent.clone()
|
||||
}
|
||||
|
||||
/// Sets one or more security and/or resource policies to an empty job.
|
||||
///
|
||||
/// The job's effective policies is the combination of the parent's
|
||||
/// effective policies and the policies specified in policy.
|
||||
///
|
||||
/// After this call succeeds any new child process or child job will have
|
||||
/// the new effective policy applied to it.
|
||||
pub fn set_policy_basic(
|
||||
&self,
|
||||
options: SetPolicyOptions,
|
||||
policies: &[BasicPolicy],
|
||||
) -> ZxResult {
|
||||
let mut inner = self.inner.lock();
|
||||
if !inner.is_empty() {
|
||||
return Err(ZxError::BAD_STATE);
|
||||
}
|
||||
for policy in policies {
|
||||
if self.parent_policy.get_action(policy.condition).is_some() {
|
||||
match options {
|
||||
SetPolicyOptions::Absolute => return Err(ZxError::ALREADY_EXISTS),
|
||||
SetPolicyOptions::Relative => {}
|
||||
}
|
||||
} else {
|
||||
inner.policy.apply(*policy);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a process to the job.
|
||||
pub(super) fn add_process(&self, process: Arc<Process>) -> ZxResult {
|
||||
let mut inner = self.inner.lock();
|
||||
if inner.killed {
|
||||
return Err(ZxError::BAD_STATE);
|
||||
}
|
||||
inner.processes.push(process);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a process from the job.
|
||||
pub(super) fn remove_process(&self, id: KoID) {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.processes.retain(|proc| proc.id() != id);
|
||||
if inner.killed && inner.processes.is_empty() && inner.children.is_empty() {
|
||||
drop(inner);
|
||||
self.terminate()
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether this job is root job.
|
||||
pub fn check_root_job(&self) -> ZxResult {
|
||||
if self.parent.is_some() {
|
||||
Err(ZxError::ACCESS_DENIED)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get KoIDs of Processes.
|
||||
pub fn process_ids(&self) -> Vec<KoID> {
|
||||
self.inner.lock().processes.iter().map(|p| p.id()).collect()
|
||||
}
|
||||
|
||||
/// Get KoIDs of children Jobs.
|
||||
pub fn children_ids(&self) -> Vec<KoID> {
|
||||
self.inner
|
||||
.lock()
|
||||
.children
|
||||
.iter()
|
||||
.filter_map(|j| j.upgrade())
|
||||
.map(|j| j.id())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Return true if this job has no processes and no child jobs.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.lock().is_empty()
|
||||
}
|
||||
|
||||
/// The job finally terminates.
|
||||
fn terminate(&self) {
|
||||
if let Some(parent) = self.parent.as_ref() {
|
||||
parent.remove_child(&self.inner.lock().self_ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Task for Job {
|
||||
/// Kill the job. The job do not terminate immediately when killed.
|
||||
/// It will terminate after all its children and processes are terminated.
|
||||
fn kill(&self) {
|
||||
let (children, processes) = {
|
||||
let mut inner = self.inner.lock();
|
||||
if inner.killed {
|
||||
return;
|
||||
}
|
||||
inner.killed = true;
|
||||
(inner.children.clone(), inner.processes.clone())
|
||||
};
|
||||
if children.is_empty() && processes.is_empty() {
|
||||
self.terminate();
|
||||
return;
|
||||
}
|
||||
for child in children {
|
||||
if let Some(child) = child.upgrade() {
|
||||
child.kill();
|
||||
}
|
||||
}
|
||||
for proc in processes {
|
||||
proc.kill();
|
||||
}
|
||||
}
|
||||
|
||||
fn suspend(&self) {
|
||||
panic!("job do not support suspend");
|
||||
}
|
||||
|
||||
fn resume(&self) {
|
||||
panic!("job do not support resume");
|
||||
}
|
||||
}
|
||||
|
||||
impl JobInner {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.processes.is_empty() && self.children.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Job {
|
||||
fn drop(&mut self) {
|
||||
self.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::task::TASK_RETCODE_SYSCALL_KILL;
|
||||
|
||||
#[test]
|
||||
fn create() {
|
||||
let root_job = Job::root();
|
||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
||||
|
||||
let child = root_job
|
||||
.get_child(job.id())
|
||||
.unwrap()
|
||||
.downcast_arc()
|
||||
.unwrap();
|
||||
assert!(Arc::ptr_eq(&child, &job));
|
||||
assert_eq!(job.related_koid(), root_job.id());
|
||||
assert_eq!(root_job.related_koid(), 0);
|
||||
|
||||
root_job.kill();
|
||||
assert_eq!(root_job.create_child().err(), Some(ZxError::BAD_STATE));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_policy() {
|
||||
let root_job = Job::root();
|
||||
|
||||
// default policy
|
||||
assert_eq!(
|
||||
root_job.policy().get_action(PolicyCondition::BadHandle),
|
||||
None
|
||||
);
|
||||
|
||||
// set policy for root job
|
||||
let policy = &[BasicPolicy {
|
||||
condition: PolicyCondition::BadHandle,
|
||||
action: PolicyAction::Deny,
|
||||
}];
|
||||
root_job
|
||||
.set_policy_basic(SetPolicyOptions::Relative, policy)
|
||||
.expect("failed to set policy");
|
||||
assert_eq!(
|
||||
root_job.policy().get_action(PolicyCondition::BadHandle),
|
||||
Some(PolicyAction::Deny)
|
||||
);
|
||||
|
||||
// override policy should success
|
||||
let policy = &[BasicPolicy {
|
||||
condition: PolicyCondition::BadHandle,
|
||||
action: PolicyAction::Allow,
|
||||
}];
|
||||
root_job
|
||||
.set_policy_basic(SetPolicyOptions::Relative, policy)
|
||||
.expect("failed to set policy");
|
||||
assert_eq!(
|
||||
root_job.policy().get_action(PolicyCondition::BadHandle),
|
||||
Some(PolicyAction::Allow)
|
||||
);
|
||||
|
||||
// create a child job
|
||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
||||
|
||||
// should inherit parent's policy.
|
||||
assert_eq!(
|
||||
job.policy().get_action(PolicyCondition::BadHandle),
|
||||
Some(PolicyAction::Allow)
|
||||
);
|
||||
|
||||
// setting policy for a non-empty job should fail.
|
||||
assert_eq!(
|
||||
root_job.set_policy_basic(SetPolicyOptions::Relative, &[]),
|
||||
Err(ZxError::BAD_STATE)
|
||||
);
|
||||
|
||||
// set new policy should success.
|
||||
let policy = &[BasicPolicy {
|
||||
condition: PolicyCondition::WrongObject,
|
||||
action: PolicyAction::Allow,
|
||||
}];
|
||||
job.set_policy_basic(SetPolicyOptions::Relative, policy)
|
||||
.expect("failed to set policy");
|
||||
assert_eq!(
|
||||
job.policy().get_action(PolicyCondition::WrongObject),
|
||||
Some(PolicyAction::Allow)
|
||||
);
|
||||
|
||||
// relatively setting existing policy should be ignored.
|
||||
let policy = &[BasicPolicy {
|
||||
condition: PolicyCondition::BadHandle,
|
||||
action: PolicyAction::Deny,
|
||||
}];
|
||||
job.set_policy_basic(SetPolicyOptions::Relative, policy)
|
||||
.expect("failed to set policy");
|
||||
assert_eq!(
|
||||
job.policy().get_action(PolicyCondition::BadHandle),
|
||||
Some(PolicyAction::Allow)
|
||||
);
|
||||
|
||||
// absolutely setting existing policy should fail.
|
||||
assert_eq!(
|
||||
job.set_policy_basic(SetPolicyOptions::Absolute, policy),
|
||||
Err(ZxError::ALREADY_EXISTS)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parent_child() {
|
||||
let root_job = Job::root();
|
||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
|
||||
assert_eq!(root_job.get_child(job.id()).unwrap().id(), job.id());
|
||||
assert_eq!(root_job.get_child(proc.id()).unwrap().id(), proc.id());
|
||||
assert_eq!(
|
||||
root_job.get_child(root_job.id()).err(),
|
||||
Some(ZxError::NOT_FOUND)
|
||||
);
|
||||
assert!(Arc::ptr_eq(&job.parent().unwrap(), &root_job));
|
||||
|
||||
let job1 = root_job.create_child().expect("failed to create job");
|
||||
let proc1 = Process::create(&root_job, "proc1").expect("failed to create process");
|
||||
assert_eq!(root_job.children_ids(), vec![job.id(), job1.id()]);
|
||||
assert_eq!(root_job.process_ids(), vec![proc.id(), proc1.id()]);
|
||||
|
||||
root_job.kill();
|
||||
assert_eq!(root_job.create_child().err(), Some(ZxError::BAD_STATE));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check() {
|
||||
let root_job = Job::root();
|
||||
assert!(root_job.is_empty());
|
||||
let job = root_job.create_child().expect("failed to create job");
|
||||
assert_eq!(root_job.check_root_job(), Ok(()));
|
||||
assert_eq!(job.check_root_job(), Err(ZxError::ACCESS_DENIED));
|
||||
|
||||
assert!(!root_job.is_empty());
|
||||
assert!(job.is_empty());
|
||||
|
||||
let _proc = Process::create(&job, "proc").expect("failed to create process");
|
||||
assert!(!job.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kill() {
|
||||
let root_job = Job::root();
|
||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
||||
let current_thread = CurrentThread(thread.clone());
|
||||
|
||||
root_job.kill();
|
||||
assert!(root_job.inner.lock().killed);
|
||||
assert!(job.inner.lock().killed);
|
||||
assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
|
||||
assert_eq!(thread.state(), ThreadState::Dying);
|
||||
|
||||
std::mem::drop(current_thread);
|
||||
assert!(root_job.inner.lock().killed);
|
||||
assert!(job.inner.lock().killed);
|
||||
assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
|
||||
assert_eq!(thread.state(), ThreadState::Dead);
|
||||
|
||||
// The job has no children.
|
||||
let root_job = Job::root();
|
||||
root_job.kill();
|
||||
assert!(root_job.inner.lock().killed);
|
||||
|
||||
// The job's process have no threads.
|
||||
let root_job = Job::root();
|
||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
root_job.kill();
|
||||
assert!(root_job.inner.lock().killed);
|
||||
assert!(job.inner.lock().killed);
|
||||
assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
/// Security and resource policies of a job.
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct JobPolicy {
|
||||
// TODO: use bitset
|
||||
action: [Option<PolicyAction>; 15],
|
||||
}
|
||||
|
||||
impl JobPolicy {
|
||||
/// Get the action of a policy `condition`.
|
||||
pub fn get_action(&self, condition: PolicyCondition) -> Option<PolicyAction> {
|
||||
self.action[condition as usize]
|
||||
}
|
||||
|
||||
/// Apply a basic policy.
|
||||
pub fn apply(&mut self, policy: BasicPolicy) {
|
||||
self.action[policy.condition as usize] = Some(policy.action);
|
||||
}
|
||||
|
||||
/// Merge the policy with `parent`'s.
|
||||
pub fn merge(&self, parent: &Self) -> Self {
|
||||
let mut new = *self;
|
||||
for i in 0..15 {
|
||||
if parent.action[i].is_some() {
|
||||
new.action[i] = parent.action[i];
|
||||
}
|
||||
}
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
/// Control the effect in the case of conflict between
|
||||
/// the existing policies and the new policies when setting new policies.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum SetPolicyOptions {
|
||||
/// Policy is applied for all conditions in policy or the call fails.
|
||||
Absolute,
|
||||
/// Policy is applied for the conditions not specifically overridden by the parent policy.
|
||||
Relative,
|
||||
}
|
||||
|
||||
/// The policy type.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct BasicPolicy {
|
||||
/// Condition when the policy is applied.
|
||||
pub condition: PolicyCondition,
|
||||
///
|
||||
pub action: PolicyAction,
|
||||
}
|
||||
|
||||
/// The condition when a policy is applied.
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum PolicyCondition {
|
||||
/// A process under this job is attempting to issue a syscall with an invalid handle.
|
||||
/// In this case, `PolicyAction::Allow` and `PolicyAction::Deny` are equivalent:
|
||||
/// if the syscall returns, it will always return the error ZX_ERR_BAD_HANDLE.
|
||||
BadHandle = 0,
|
||||
/// A process under this job is attempting to issue a syscall with a handle that does not support such operation.
|
||||
WrongObject = 1,
|
||||
/// A process under this job is attempting to map an address region with write-execute access.
|
||||
VmarWx = 2,
|
||||
/// A special condition that stands for all of the above ZX_NEW conditions
|
||||
/// such as NEW_VMO, NEW_CHANNEL, NEW_EVENT, NEW_EVENTPAIR, NEW_PORT, NEW_SOCKET, NEW_FIFO,
|
||||
/// And any future ZX_NEW policy.
|
||||
/// This will include any new kernel objects which do not require a parent object for creation.
|
||||
NewAny = 3,
|
||||
/// A process under this job is attempting to create a new vm object.
|
||||
NewVMO = 4,
|
||||
/// A process under this job is attempting to create a new channel.
|
||||
NewChannel = 5,
|
||||
/// A process under this job is attempting to create a new event.
|
||||
NewEvent = 6,
|
||||
/// A process under this job is attempting to create a new event pair.
|
||||
NewEventPair = 7,
|
||||
/// A process under this job is attempting to create a new port.
|
||||
NewPort = 8,
|
||||
/// A process under this job is attempting to create a new socket.
|
||||
NewSocket = 9,
|
||||
/// A process under this job is attempting to create a new fifo.
|
||||
NewFIFO = 10,
|
||||
/// A process under this job is attempting to create a new timer.
|
||||
NewTimer = 11,
|
||||
/// A process under this job is attempting to create a new process.
|
||||
NewProcess = 12,
|
||||
/// A process under this job is attempting to create a new profile.
|
||||
NewProfile = 13,
|
||||
/// A process under this job is attempting to use zx_vmo_replace_as_executable()
|
||||
/// with a ZX_HANDLE_INVALID as the second argument rather than a valid ZX_RSRC_KIND_VMEX.
|
||||
AmbientMarkVMOExec = 14,
|
||||
}
|
||||
|
||||
/// The action taken when the condition happens specified by a policy.
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum PolicyAction {
|
||||
/// Allow condition.
|
||||
Allow = 0,
|
||||
/// Prevent condition.
|
||||
Deny = 1,
|
||||
/// Generate an exception via the debug port. An exception generated this
|
||||
/// way acts as a breakpoint. The thread may be resumed after the exception.
|
||||
AllowException = 2,
|
||||
/// Just like `AllowException`, but after resuming condition is denied.
|
||||
DenyException = 3,
|
||||
/// Terminate the process.
|
||||
Kill = 4,
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
use super::*;
|
||||
|
||||
mod job;
|
||||
mod job_policy;
|
||||
mod process;
|
||||
mod thread;
|
||||
|
||||
pub use {self::job::*, self::job_policy::*, self::process::*, self::thread::*};
|
||||
|
||||
/// Task (Thread, Process, or Job)
|
||||
pub trait Task: Sync + Send {
|
||||
/// Kill the task. The task do not terminate immediately when killed.
|
||||
/// It will terminate after all its children are terminated or some cleanups are finished.
|
||||
fn kill(&self);
|
||||
|
||||
/// Suspend the task. Currently only thread or process handles may be suspended.
|
||||
fn suspend(&self);
|
||||
|
||||
/// Resume the task
|
||||
fn resume(&self);
|
||||
}
|
||||
|
||||
/// The return code set when a task is killed via zx_task_kill().
|
||||
pub const TASK_RETCODE_SYSCALL_KILL: i64 = -1028;
|
||||
@ -0,0 +1,505 @@
|
||||
use {
|
||||
super::{job::Job, job_policy::*, thread::*, *},
|
||||
crate::{error::*, object::*, vm::*},
|
||||
alloc::{sync::Arc, vec::Vec},
|
||||
hashbrown::HashMap,
|
||||
spin::Mutex,
|
||||
};
|
||||
|
||||
pub struct Process {
|
||||
base: KObjectBase,
|
||||
job: Arc<Job>,
|
||||
policy: JobPolicy,
|
||||
vmar: Arc<VmAddressRegion>,
|
||||
inner: Mutex<ProcessInner>,
|
||||
}
|
||||
|
||||
impl_kobject!(Process
|
||||
fn get_child(&self, id: KoID) -> ZxResult<Arc<dyn KernelObject>> {
|
||||
let inner = self.inner.lock();
|
||||
let thread = inner.threads.iter().find(|o| o.id() == id).ok_or(ZxError::NOT_FOUND)?;
|
||||
Ok(thread.clone())
|
||||
}
|
||||
fn related_koid(&self) -> KoID {
|
||||
self.job.id()
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Default)]
|
||||
struct ProcessInner {
|
||||
max_handle_id: u32,
|
||||
status: Status,
|
||||
handles: HashMap<HandleValue, Handle>,
|
||||
threads: Vec<Arc<Thread>>,
|
||||
}
|
||||
|
||||
/// Status of a process.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Status {
|
||||
/// Initial state, no thread present in process.
|
||||
Init,
|
||||
/// First thread has started and is running.
|
||||
Running,
|
||||
/// Process has exited with the code.
|
||||
Exited(i64),
|
||||
}
|
||||
|
||||
impl Default for Status {
|
||||
fn default() -> Self {
|
||||
Status::Init
|
||||
}
|
||||
}
|
||||
|
||||
impl Process {
|
||||
/// Create a new process in the `job`.
|
||||
pub fn create(job: &Arc<Job>, name: &str) -> ZxResult<Arc<Self>> {
|
||||
let proc = Arc::new(Process {
|
||||
base: KObjectBase::with_name(name),
|
||||
job: job.clone(),
|
||||
policy: job.policy(),
|
||||
vmar: VmAddressRegion::new_root(),
|
||||
inner: Mutex::new(ProcessInner::default()),
|
||||
});
|
||||
job.add_process(proc.clone())?;
|
||||
Ok(proc)
|
||||
}
|
||||
|
||||
/// Get a handle from the process
|
||||
fn get_handle(&self, handle_value: HandleValue) -> ZxResult<Handle> {
|
||||
self.inner.lock().get_handle(handle_value)
|
||||
}
|
||||
|
||||
/// 添加一个新的对象句柄
|
||||
pub fn add_handle(&self, handle: Handle) -> HandleValue {
|
||||
self.inner.lock().add_handle(handle)
|
||||
}
|
||||
|
||||
/// 删除一个对象句柄
|
||||
pub fn remove_handle(&self, handle_value: HandleValue) -> ZxResult<Handle> {
|
||||
self.inner.lock().remove_handle(handle_value)
|
||||
}
|
||||
|
||||
/// Add all handles to the process
|
||||
pub fn add_handles(&self, handles: Vec<Handle>) -> Vec<HandleValue> {
|
||||
let mut inner = self.inner.lock();
|
||||
handles.into_iter().map(|h| inner.add_handle(h)).collect()
|
||||
}
|
||||
|
||||
/// Remove all handles from the process.
|
||||
pub fn remove_handles(&self, handle_values: &[HandleValue]) -> ZxResult<Vec<Handle>> {
|
||||
let mut inner = self.inner.lock();
|
||||
handle_values
|
||||
.iter()
|
||||
.map(|h| inner.remove_handle(*h))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get the kernel object corresponding to this `handle_value`
|
||||
pub fn get_object<T: KernelObject>(&self, handle_value: HandleValue) -> ZxResult<Arc<T>> {
|
||||
let handle = self.get_handle(handle_value)?;
|
||||
let object = handle
|
||||
.object
|
||||
.downcast_arc::<T>()
|
||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
||||
Ok(object)
|
||||
}
|
||||
|
||||
/// 根据句柄值查找内核对象,并检查权限
|
||||
pub fn get_object_with_rights<T: KernelObject>(
|
||||
&self,
|
||||
handle_value: HandleValue,
|
||||
desired_rights: Rights,
|
||||
) -> ZxResult<Arc<T>> {
|
||||
let handle = self.get_handle(handle_value)?;
|
||||
// check type before rights
|
||||
let object = handle
|
||||
.object
|
||||
.downcast_arc::<T>()
|
||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
||||
if !handle.rights.contains(desired_rights) {
|
||||
return Err(ZxError::ACCESS_DENIED);
|
||||
}
|
||||
Ok(object)
|
||||
}
|
||||
|
||||
/// Get the kernel object corresponding to this `handle_value` and this handle's rights.
|
||||
pub fn get_object_and_rights<T: KernelObject>(
|
||||
&self,
|
||||
handle_value: HandleValue,
|
||||
) -> ZxResult<(Arc<T>, Rights)> {
|
||||
let handle = self.get_handle(handle_value)?;
|
||||
let object = handle
|
||||
.object
|
||||
.downcast_arc::<T>()
|
||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
||||
Ok((object, handle.rights))
|
||||
}
|
||||
|
||||
/// Remove a handle referring to a kernel object of the given type from the process.
|
||||
pub fn remove_object<T: KernelObject>(&self, handle_value: HandleValue) -> ZxResult<Arc<T>> {
|
||||
let handle = self.remove_handle(handle_value)?;
|
||||
let object = handle
|
||||
.object
|
||||
.downcast_arc::<T>()
|
||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
||||
Ok(object)
|
||||
}
|
||||
|
||||
pub fn start(
|
||||
&self,
|
||||
thread: &Arc<Thread>,
|
||||
entry: usize,
|
||||
stack: usize,
|
||||
arg1: Option<Handle>,
|
||||
arg2: usize,
|
||||
thread_fn: ThreadFn,
|
||||
) -> ZxResult {
|
||||
let handle_value;
|
||||
{
|
||||
let mut inner = self.inner.lock();
|
||||
if !inner.contains_thread(thread) {
|
||||
return Err(ZxError::ACCESS_DENIED);
|
||||
}
|
||||
if inner.status != Status::Init {
|
||||
return Err(ZxError::BAD_STATE);
|
||||
}
|
||||
inner.status = Status::Running;
|
||||
handle_value = arg1.map_or(INVALID_HANDLE, |handle| inner.add_handle(handle));
|
||||
}
|
||||
thread.set_first_thread();
|
||||
match thread.start(entry, stack, handle_value as usize, arg2, thread_fn) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
let mut inner = self.inner.lock();
|
||||
if handle_value != INVALID_HANDLE {
|
||||
inner.remove_handle(handle_value).ok();
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Exit current process with `retcode`.
|
||||
/// The process do not terminate immediately when exited.
|
||||
/// It will terminate after all its child threads are terminated.
|
||||
pub fn exit(&self, retcode: i64) {
|
||||
let mut inner = self.inner.lock();
|
||||
if let Status::Exited(_) = inner.status {
|
||||
return;
|
||||
}
|
||||
inner.status = Status::Exited(retcode);
|
||||
if inner.threads.is_empty() {
|
||||
inner.handles.clear();
|
||||
drop(inner);
|
||||
self.terminate();
|
||||
return;
|
||||
}
|
||||
for thread in inner.threads.iter() {
|
||||
thread.kill();
|
||||
}
|
||||
inner.handles.clear();
|
||||
}
|
||||
|
||||
/// The process finally terminates.
|
||||
fn terminate(&self) {
|
||||
let mut inner = self.inner.lock();
|
||||
let _retcode = match inner.status {
|
||||
Status::Exited(retcode) => retcode,
|
||||
_ => {
|
||||
inner.status = Status::Exited(0);
|
||||
0
|
||||
}
|
||||
};
|
||||
self.job.remove_process(self.base.id);
|
||||
}
|
||||
|
||||
/// Check whether `condition` is allowed in the parent job's policy.
|
||||
pub fn check_policy(&self, condition: PolicyCondition) -> ZxResult {
|
||||
match self
|
||||
.policy
|
||||
.get_action(condition)
|
||||
.unwrap_or(PolicyAction::Allow)
|
||||
{
|
||||
PolicyAction::Allow => Ok(()),
|
||||
PolicyAction::Deny => Err(ZxError::ACCESS_DENIED),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get process status.
|
||||
pub fn status(&self) -> Status {
|
||||
self.inner.lock().status
|
||||
}
|
||||
|
||||
/// Get the `VmAddressRegion` of the process.
|
||||
pub fn vmar(&self) -> Arc<VmAddressRegion> {
|
||||
self.vmar.clone()
|
||||
}
|
||||
|
||||
/// Get the job of the process.
|
||||
pub fn job(&self) -> Arc<Job> {
|
||||
self.job.clone()
|
||||
}
|
||||
|
||||
/// Add a thread to the process.
|
||||
pub(super) fn add_thread(&self, thread: Arc<Thread>) -> ZxResult {
|
||||
let mut inner = self.inner.lock();
|
||||
if let Status::Exited(_) = inner.status {
|
||||
return Err(ZxError::BAD_STATE);
|
||||
}
|
||||
inner.threads.push(thread);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a thread from the process.
|
||||
///
|
||||
/// If no more threads left, exit the process.
|
||||
pub(super) fn remove_thread(&self, tid: KoID) {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.threads.retain(|t| t.id() != tid);
|
||||
if inner.threads.is_empty() {
|
||||
drop(inner);
|
||||
self.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
/// Get KoIDs of Threads.
|
||||
pub fn thread_ids(&self) -> Vec<KoID> {
|
||||
self.inner.lock().threads.iter().map(|t| t.id()).collect()
|
||||
}
|
||||
|
||||
/// Get information of this process.
|
||||
pub fn get_info(&self) -> ProcessInfo {
|
||||
let mut info = ProcessInfo {
|
||||
..Default::default()
|
||||
};
|
||||
match self.inner.lock().status {
|
||||
Status::Init => {
|
||||
info.started = false;
|
||||
info.has_exited = false;
|
||||
}
|
||||
Status::Running => {
|
||||
info.started = true;
|
||||
info.has_exited = false;
|
||||
}
|
||||
Status::Exited(ret) => {
|
||||
info.return_code = ret;
|
||||
info.has_exited = true;
|
||||
info.started = true;
|
||||
}
|
||||
}
|
||||
info
|
||||
}
|
||||
}
|
||||
|
||||
/// Information of a process.
|
||||
#[allow(missing_docs)]
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
pub struct ProcessInfo {
|
||||
pub return_code: i64,
|
||||
pub started: bool,
|
||||
pub has_exited: bool,
|
||||
}
|
||||
|
||||
impl Task for Process {
|
||||
fn kill(&self) {
|
||||
self.exit(TASK_RETCODE_SYSCALL_KILL);
|
||||
}
|
||||
|
||||
fn suspend(&self) {
|
||||
let inner = self.inner.lock();
|
||||
for thread in inner.threads.iter() {
|
||||
thread.suspend();
|
||||
}
|
||||
}
|
||||
|
||||
fn resume(&self) {
|
||||
let inner = self.inner.lock();
|
||||
for thread in inner.threads.iter() {
|
||||
thread.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProcessInner {
|
||||
/// Add a handle to the process
|
||||
fn add_handle(&mut self, handle: Handle) -> HandleValue {
|
||||
let key = (self.max_handle_id << 2) | 0x3u32;
|
||||
self.max_handle_id += 1;
|
||||
self.handles.insert(key, handle);
|
||||
key
|
||||
}
|
||||
|
||||
fn remove_handle(&mut self, handle_value: HandleValue) -> ZxResult<Handle> {
|
||||
let handle = self
|
||||
.handles
|
||||
.remove(&handle_value)
|
||||
.ok_or(ZxError::BAD_HANDLE)?;
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
fn get_handle(&mut self, handle_value: HandleValue) -> ZxResult<Handle> {
|
||||
let handle = self.handles.get(&handle_value).ok_or(ZxError::BAD_HANDLE)?;
|
||||
Ok(handle.clone())
|
||||
}
|
||||
|
||||
/// Whether `thread` is in this process.
|
||||
fn contains_thread(&self, thread: &Arc<Thread>) -> bool {
|
||||
self.threads.iter().any(|t| Arc::ptr_eq(t, thread))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn create() {
|
||||
let root_job = Job::root();
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
|
||||
assert_eq!(proc.related_koid(), root_job.id());
|
||||
assert!(Arc::ptr_eq(&root_job, &proc.job()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handle() {
|
||||
let root_job = Job::root();
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
let handle = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
||||
|
||||
let handle_value = proc.add_handle(handle);
|
||||
|
||||
// getting object should success
|
||||
let object: Arc<Process> = proc
|
||||
.get_object_with_rights(handle_value, Rights::DEFAULT_PROCESS)
|
||||
.expect("failed to get object");
|
||||
assert!(Arc::ptr_eq(&object, &proc));
|
||||
|
||||
let (object, rights) = proc
|
||||
.get_object_and_rights::<Process>(handle_value)
|
||||
.expect("failed to get object");
|
||||
assert!(Arc::ptr_eq(&object, &proc));
|
||||
assert_eq!(rights, Rights::DEFAULT_PROCESS);
|
||||
|
||||
// getting object with an extra rights should fail.
|
||||
assert_eq!(
|
||||
proc.get_object_with_rights::<Process>(handle_value, Rights::MANAGE_JOB)
|
||||
.err(),
|
||||
Some(ZxError::ACCESS_DENIED)
|
||||
);
|
||||
|
||||
// getting object with invalid type should fail.
|
||||
assert_eq!(
|
||||
proc.get_object_with_rights::<Job>(handle_value, Rights::DEFAULT_PROCESS)
|
||||
.err(),
|
||||
Some(ZxError::WRONG_TYPE)
|
||||
);
|
||||
|
||||
proc.remove_handle(handle_value).unwrap();
|
||||
|
||||
// getting object with invalid handle should fail.
|
||||
assert_eq!(
|
||||
proc.get_object_with_rights::<Process>(handle_value, Rights::DEFAULT_PROCESS)
|
||||
.err(),
|
||||
Some(ZxError::BAD_HANDLE)
|
||||
);
|
||||
|
||||
let handle1 = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
||||
let handle2 = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
||||
|
||||
let handle_values = proc.add_handles(vec![handle1, handle2]);
|
||||
let object1: Arc<Process> = proc
|
||||
.get_object_with_rights(handle_values[0], Rights::DEFAULT_PROCESS)
|
||||
.expect("failed to get object");
|
||||
assert!(Arc::ptr_eq(&object1, &proc));
|
||||
|
||||
proc.remove_handles(&handle_values).unwrap();
|
||||
assert_eq!(
|
||||
proc.get_object_with_rights::<Process>(handle_values[0], Rights::DEFAULT_PROCESS)
|
||||
.err(),
|
||||
Some(ZxError::BAD_HANDLE)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_child() {
|
||||
let root_job = Job::root();
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
||||
|
||||
assert_eq!(proc.get_child(thread.id()).unwrap().id(), thread.id());
|
||||
assert_eq!(proc.get_child(proc.id()).err(), Some(ZxError::NOT_FOUND));
|
||||
|
||||
let thread1 = Thread::create(&proc, "thread1").expect("failed to create thread");
|
||||
assert_eq!(proc.thread_ids(), vec![thread.id(), thread1.id()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn contains_thread() {
|
||||
let root_job = Job::root();
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
||||
|
||||
let proc1 = Process::create(&root_job, "proc1").expect("failed to create process");
|
||||
let thread1 = Thread::create(&proc1, "thread1").expect("failed to create thread");
|
||||
|
||||
let inner = proc.inner.lock();
|
||||
assert!(inner.contains_thread(&thread) && !inner.contains_thread(&thread1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_policy() {
|
||||
let root_job = Job::root();
|
||||
let policy1 = BasicPolicy {
|
||||
condition: PolicyCondition::BadHandle,
|
||||
action: PolicyAction::Allow,
|
||||
};
|
||||
let policy2 = BasicPolicy {
|
||||
condition: PolicyCondition::NewChannel,
|
||||
action: PolicyAction::Deny,
|
||||
};
|
||||
|
||||
assert!(root_job
|
||||
.set_policy_basic(SetPolicyOptions::Absolute, &[policy1, policy2])
|
||||
.is_ok());
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
|
||||
assert!(proc.check_policy(PolicyCondition::BadHandle).is_ok());
|
||||
assert!(proc.check_policy(PolicyCondition::NewProcess).is_ok());
|
||||
assert_eq!(
|
||||
proc.check_policy(PolicyCondition::NewChannel).err(),
|
||||
Some(ZxError::ACCESS_DENIED)
|
||||
);
|
||||
|
||||
let _job = root_job.create_child().unwrap();
|
||||
assert_eq!(
|
||||
root_job
|
||||
.set_policy_basic(SetPolicyOptions::Absolute, &[policy1, policy2])
|
||||
.err(),
|
||||
Some(ZxError::BAD_STATE)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exit() {
|
||||
let root_job = Job::root();
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
||||
|
||||
let info = proc.get_info();
|
||||
assert!(!info.has_exited && !info.started && info.return_code == 0);
|
||||
|
||||
proc.exit(666);
|
||||
let info = proc.get_info();
|
||||
assert!(info.has_exited && info.started && info.return_code == 666);
|
||||
assert_eq!(thread.state(), ThreadState::Dying);
|
||||
// TODO: when is the thread dead?
|
||||
|
||||
assert_eq!(
|
||||
Thread::create(&proc, "thread1").err(),
|
||||
Some(ZxError::BAD_STATE)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,569 @@
|
||||
use {
|
||||
super::process::Process,
|
||||
super::*,
|
||||
crate::object::*,
|
||||
alloc::{boxed::Box, sync::Arc},
|
||||
bitflags::bitflags,
|
||||
core::{
|
||||
future::Future,
|
||||
ops::Deref,
|
||||
pin::Pin,
|
||||
task::{Context, Poll, Waker},
|
||||
},
|
||||
trapframe::{UserContext},
|
||||
spin::Mutex,
|
||||
};
|
||||
|
||||
pub use self::thread_state::*;
|
||||
|
||||
mod thread_state;
|
||||
|
||||
pub struct Thread {
|
||||
base: KObjectBase,
|
||||
proc: Arc<Process>,
|
||||
inner: Mutex<ThreadInner>,
|
||||
}
|
||||
|
||||
impl_kobject!(Thread
|
||||
fn related_koid(&self) -> KoID {
|
||||
self.proc.id()
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Default)]
|
||||
struct ThreadInner {
|
||||
/// Thread context
|
||||
///
|
||||
/// It will be taken away when running this thread.
|
||||
context: Option<Box<UserContext>>,
|
||||
/// The number of existing `SuspendToken`.
|
||||
suspend_count: usize,
|
||||
/// The waker of task when suspending.
|
||||
waker: Option<Waker>,
|
||||
/// Thread state
|
||||
///
|
||||
/// NOTE: This variable will never be `Suspended`. On suspended, the
|
||||
/// `suspend_count` is non-zero, and this represents the state before suspended.
|
||||
state: ThreadState,
|
||||
/// Should The ProcessStarting exception generated at start of this thread
|
||||
first_thread: bool,
|
||||
/// Should The ThreadExiting exception do not block this thread
|
||||
killed: bool,
|
||||
/// The time this thread has run on cpu
|
||||
time: u128,
|
||||
flags: ThreadFlag,
|
||||
}
|
||||
|
||||
impl ThreadInner {
|
||||
fn state(&self) -> ThreadState {
|
||||
// Dying > Exception > Suspend > Blocked
|
||||
if self.suspend_count == 0
|
||||
|| self.context.is_none()
|
||||
|| self.state == ThreadState::BlockedException
|
||||
|| self.state == ThreadState::Dying
|
||||
|| self.state == ThreadState::Dead
|
||||
{
|
||||
self.state
|
||||
} else {
|
||||
ThreadState::Suspended
|
||||
}
|
||||
}
|
||||
|
||||
/// Change state and update signal.
|
||||
fn change_state(&mut self, state: ThreadState) {
|
||||
self.state = state;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Thread flags.
|
||||
#[derive(Default)]
|
||||
pub struct ThreadFlag: usize {
|
||||
/// The thread currently has a VCPU.
|
||||
const VCPU = 1 << 3;
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of a new thread function.
|
||||
pub type ThreadFn = fn(thread: CurrentThread) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
|
||||
|
||||
impl Thread {
|
||||
/// Create a new thread.
|
||||
pub fn create(proc: &Arc<Process>, name: &str) -> ZxResult<Arc<Self>> {
|
||||
let thread = Arc::new(Thread {
|
||||
base: KObjectBase::with_name(name),
|
||||
proc: proc.clone(),
|
||||
inner: Mutex::new(ThreadInner {
|
||||
context: Some(Box::new(UserContext::default())),
|
||||
..Default::default()
|
||||
}),
|
||||
});
|
||||
proc.add_thread(thread.clone())?;
|
||||
Ok(thread)
|
||||
}
|
||||
|
||||
/// Get the process.
|
||||
pub fn proc(&self) -> &Arc<Process> {
|
||||
&self.proc
|
||||
}
|
||||
|
||||
/// Start execution on the thread.
|
||||
pub fn start(
|
||||
self: &Arc<Self>,
|
||||
entry: usize,
|
||||
stack: usize,
|
||||
arg1: usize,
|
||||
arg2: usize,
|
||||
thread_fn: ThreadFn,
|
||||
) -> ZxResult {
|
||||
{
|
||||
let mut inner = self.inner.lock();
|
||||
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
|
||||
context.general.rip = entry;
|
||||
context.general.rsp = stack;
|
||||
context.general.rdi = arg1;
|
||||
context.general.rsi = arg2;
|
||||
context.general.rflags |= 0x3202;
|
||||
inner.change_state(ThreadState::Running);
|
||||
}
|
||||
kernel_hal::Thread::spawn(thread_fn(CurrentThread(self.clone())), 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Stop the thread. Internal implementation of `exit` and `kill`.
|
||||
///
|
||||
/// The thread do not terminate immediately when stopped. It is just made dying.
|
||||
/// It will terminate after some cleanups (when `terminate` are called **explicitly** by upper layer).
|
||||
fn stop(&self, killed: bool) {
|
||||
let mut inner = self.inner.lock();
|
||||
if inner.state == ThreadState::Dead {
|
||||
return;
|
||||
}
|
||||
if killed {
|
||||
inner.killed = true;
|
||||
}
|
||||
if inner.state == ThreadState::Dying {
|
||||
return;
|
||||
}
|
||||
inner.change_state(ThreadState::Dying);
|
||||
if let Some(waker) = inner.waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
/// Read one aspect of thread state.
|
||||
pub fn read_state(&self, kind: ThreadStateKind, buf: &mut [u8]) -> ZxResult<usize> {
|
||||
let inner = self.inner.lock();
|
||||
let state = inner.state();
|
||||
if state != ThreadState::BlockedException && state != ThreadState::Suspended {
|
||||
return Err(ZxError::BAD_STATE);
|
||||
}
|
||||
let context = inner.context.as_ref().ok_or(ZxError::BAD_STATE)?;
|
||||
context.read_state(kind, buf)
|
||||
}
|
||||
|
||||
/// Write one aspect of thread state.
|
||||
pub fn write_state(&self, kind: ThreadStateKind, buf: &[u8]) -> ZxResult {
|
||||
let mut inner = self.inner.lock();
|
||||
let state = inner.state();
|
||||
if state != ThreadState::BlockedException && state != ThreadState::Suspended {
|
||||
return Err(ZxError::BAD_STATE);
|
||||
}
|
||||
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
|
||||
context.write_state(kind, buf)
|
||||
}
|
||||
|
||||
/// Get the thread's information.
|
||||
pub fn get_thread_info(&self) -> ThreadInfo {
|
||||
let inner = self.inner.lock();
|
||||
ThreadInfo {
|
||||
state: inner.state() as u32,
|
||||
}
|
||||
}
|
||||
/// Get the thread state.
|
||||
pub fn state(&self) -> ThreadState {
|
||||
self.inner.lock().state()
|
||||
}
|
||||
|
||||
/// Add the parameter to the time this thread has run on cpu.
|
||||
pub fn time_add(&self, time: u128) {
|
||||
self.inner.lock().time += time;
|
||||
}
|
||||
|
||||
/// Get the time this thread has run on cpu.
|
||||
pub fn get_time(&self) -> u64 {
|
||||
self.inner.lock().time as u64
|
||||
}
|
||||
|
||||
/// Set this thread as the first thread of a process.
|
||||
pub(super) fn set_first_thread(&self) {
|
||||
self.inner.lock().first_thread = true;
|
||||
}
|
||||
|
||||
/// Whether this thread is the first thread of a process.
|
||||
pub fn is_first_thread(&self) -> bool {
|
||||
self.inner.lock().first_thread
|
||||
}
|
||||
|
||||
/// Get the thread's flags.
|
||||
pub fn flags(&self) -> ThreadFlag {
|
||||
self.inner.lock().flags
|
||||
}
|
||||
|
||||
/// Apply `f` to the thread's flags.
|
||||
pub fn update_flags(&self, f: impl FnOnce(&mut ThreadFlag)) {
|
||||
f(&mut self.inner.lock().flags)
|
||||
}
|
||||
|
||||
/// Set the thread local fsbase register on x86_64.
|
||||
pub fn set_fsbase(&self, fsbase: usize) -> ZxResult {
|
||||
let mut inner = self.inner.lock();
|
||||
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
|
||||
context.general.fsbase = fsbase;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the thread local gsbase register on x86_64.
|
||||
pub fn set_gsbase(&self, gsbase: usize) -> ZxResult {
|
||||
let mut inner = self.inner.lock();
|
||||
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
|
||||
context.general.gsbase = gsbase;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Task for Thread {
|
||||
fn kill(&self) {
|
||||
self.stop(true)
|
||||
}
|
||||
|
||||
fn suspend(&self) {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.suspend_count += 1;
|
||||
let state = inner.state;
|
||||
inner.change_state(state);
|
||||
}
|
||||
|
||||
fn resume(&self) {
|
||||
let mut inner = self.inner.lock();
|
||||
assert_ne!(inner.suspend_count, 0);
|
||||
inner.suspend_count -= 1;
|
||||
if inner.suspend_count == 0 {
|
||||
let state = inner.state;
|
||||
inner.change_state(state);
|
||||
if let Some(waker) = inner.waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A handle to current thread.
|
||||
///
|
||||
/// This is a wrapper of [`Thread`] that provides additional methods for the thread runner.
|
||||
/// It can only be obtained from the argument of `thread_fn` in a new thread started by [`Thread::start`].
|
||||
///
|
||||
/// It will terminate current thread on drop.
|
||||
///
|
||||
/// [`Thread`]: crate::task::Thread
|
||||
/// [`Thread::start`]: crate::task::Thread::start
|
||||
pub struct CurrentThread(pub(super) Arc<Thread>);
|
||||
|
||||
impl Deref for CurrentThread {
|
||||
type Target = Arc<Thread>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CurrentThread {
|
||||
/// Terminate the current running thread.
|
||||
fn drop(&mut self) {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.change_state(ThreadState::Dead);
|
||||
self.proc().remove_thread(self.base.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl CurrentThread {
|
||||
/// Exit the current thread.
|
||||
///
|
||||
/// The thread do not terminate immediately when exited. It is just made dying.
|
||||
/// It will terminate after some cleanups on this struct drop.
|
||||
pub fn exit(&self) {
|
||||
self.stop(false);
|
||||
}
|
||||
|
||||
/// Wait until the thread is ready to run (not suspended),
|
||||
/// and then take away its context to run the thread.
|
||||
pub fn wait_for_run(&self) -> impl Future<Output = Box<UserContext>> {
|
||||
#[must_use = "wait_for_run does nothing unless polled/`await`-ed"]
|
||||
struct RunnableChecker {
|
||||
thread: Arc<Thread>,
|
||||
}
|
||||
impl Future for RunnableChecker {
|
||||
type Output = Box<UserContext>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||
let mut inner = self.thread.inner.lock();
|
||||
if inner.state() != ThreadState::Suspended {
|
||||
// resume: return the context token from thread object
|
||||
// There is no need to call change_state here
|
||||
// since take away the context of a non-suspended thread won't change it's state
|
||||
Poll::Ready(inner.context.take().unwrap())
|
||||
} else {
|
||||
// suspend: put waker into the thread object
|
||||
inner.waker = Some(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
RunnableChecker {
|
||||
thread: self.0.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The thread ends running and takes back the context.
|
||||
pub fn end_running(&self, context: Box<UserContext>) {
|
||||
let mut inner = self.inner.lock();
|
||||
inner.context = Some(context);
|
||||
let state = inner.state;
|
||||
inner.change_state(state);
|
||||
}
|
||||
|
||||
/// Access saved context of current thread.
|
||||
///
|
||||
/// Will panic if the context is not availiable.
|
||||
pub fn with_context<T, F>(&self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut UserContext) -> T,
|
||||
{
|
||||
let mut inner = self.inner.lock();
|
||||
let mut cx = inner.context.as_mut().unwrap();
|
||||
f(&mut cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// The thread state.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum ThreadState {
|
||||
/// The thread has been created but it has not started running yet.
|
||||
New = 0,
|
||||
/// The thread is running user code normally.
|
||||
Running = 1,
|
||||
/// Stopped due to `zx_task_suspend()`.
|
||||
Suspended = 2,
|
||||
/// In a syscall or handling an exception.
|
||||
Blocked = 3,
|
||||
/// The thread is in the process of being terminated, but it has not been stopped yet.
|
||||
Dying = 4,
|
||||
/// The thread has stopped running.
|
||||
Dead = 5,
|
||||
/// The thread is stopped in an exception.
|
||||
BlockedException = 0x103,
|
||||
/// The thread is stopped in `zx_nanosleep()`.
|
||||
BlockedSleeping = 0x203,
|
||||
/// The thread is stopped in `zx_futex_wait()`.
|
||||
BlockedFutex = 0x303,
|
||||
/// The thread is stopped in `zx_port_wait()`.
|
||||
BlockedPort = 0x403,
|
||||
/// The thread is stopped in `zx_channel_call()`.
|
||||
BlockedChannel = 0x503,
|
||||
/// The thread is stopped in `zx_object_wait_one()`.
|
||||
BlockedWaitOne = 0x603,
|
||||
/// The thread is stopped in `zx_object_wait_many()`.
|
||||
BlockedWaitMany = 0x703,
|
||||
/// The thread is stopped in `zx_interrupt_wait()`.
|
||||
BlockedInterrupt = 0x803,
|
||||
/// Pager.
|
||||
BlockedPager = 0x903,
|
||||
}
|
||||
|
||||
impl Default for ThreadState {
|
||||
fn default() -> Self {
|
||||
ThreadState::New
|
||||
}
|
||||
}
|
||||
|
||||
/// The thread information.
|
||||
#[repr(C)]
|
||||
pub struct ThreadInfo {
|
||||
state: u32,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::job::Job;
|
||||
use super::*;
|
||||
use kernel_hal::timer_now;
|
||||
use kernel_hal::GeneralRegs;
|
||||
use core::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn create() {
|
||||
let root_job = Job::root();
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
||||
assert_eq!(thread.flags(), ThreadFlag::empty());
|
||||
|
||||
assert_eq!(thread.related_koid(), proc.id());
|
||||
let child = proc.get_child(thread.id()).unwrap().downcast_arc().unwrap();
|
||||
assert!(Arc::ptr_eq(&child, &thread));
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn start() {
|
||||
kernel_hal_unix::init();
|
||||
let root_job = Job::root();
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
||||
let thread1 = Thread::create(&proc, "thread1").expect("failed to create thread");
|
||||
|
||||
// function for new thread
|
||||
async fn new_thread(thread: CurrentThread) {
|
||||
let cx = thread.wait_for_run().await;
|
||||
assert_eq!(cx.general.rip, 1);
|
||||
assert_eq!(cx.general.rsp, 4);
|
||||
assert_eq!(cx.general.rdi, 3);
|
||||
assert_eq!(cx.general.rsi, 2);
|
||||
async_std::task::sleep(Duration::from_millis(10)).await;
|
||||
thread.end_running(cx);
|
||||
}
|
||||
|
||||
// start a new thread
|
||||
let handle = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
||||
proc.start(&thread, 1, 4, Some(handle.clone()), 2, |thread| {
|
||||
Box::pin(new_thread(thread))
|
||||
})
|
||||
.expect("failed to start thread");
|
||||
|
||||
// check info and state
|
||||
let info = proc.get_info();
|
||||
assert!(info.started && !info.has_exited && info.return_code == 0);
|
||||
assert_eq!(proc.status(), Status::Running);
|
||||
assert_eq!(thread.state(), ThreadState::Running);
|
||||
|
||||
// start again should fail
|
||||
assert_eq!(
|
||||
proc.start(&thread, 1, 4, Some(handle.clone()), 2, |thread| Box::pin(
|
||||
new_thread(thread)
|
||||
)),
|
||||
Err(ZxError::BAD_STATE)
|
||||
);
|
||||
|
||||
// start another thread should fail
|
||||
assert_eq!(
|
||||
proc.start(&thread1, 1, 4, Some(handle.clone()), 2, |thread| Box::pin(
|
||||
new_thread(thread)
|
||||
)),
|
||||
Err(ZxError::BAD_STATE)
|
||||
);
|
||||
|
||||
// wait 100ms for the new thread to exit
|
||||
async_std::task::sleep(core::time::Duration::from_millis(100)).await;
|
||||
|
||||
// no other references to `Thread`
|
||||
assert_eq!(Arc::strong_count(&thread), 1);
|
||||
assert_eq!(thread.state(), ThreadState::Dead);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn info() {
|
||||
let root_job = Job::root();
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
||||
|
||||
let info = thread.get_thread_info();
|
||||
assert!(info.state == thread.state() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_write_state() {
|
||||
let root_job = Job::root();
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
||||
|
||||
const SIZE: usize = core::mem::size_of::<GeneralRegs>();
|
||||
let mut buf = [0; 10];
|
||||
assert_eq!(
|
||||
thread.read_state(ThreadStateKind::General, &mut buf).err(),
|
||||
Some(ZxError::BAD_STATE)
|
||||
);
|
||||
assert_eq!(
|
||||
thread.write_state(ThreadStateKind::General, &buf).err(),
|
||||
Some(ZxError::BAD_STATE)
|
||||
);
|
||||
|
||||
thread.suspend();
|
||||
|
||||
assert_eq!(
|
||||
thread.read_state(ThreadStateKind::General, &mut buf).err(),
|
||||
Some(ZxError::BUFFER_TOO_SMALL)
|
||||
);
|
||||
assert_eq!(
|
||||
thread.write_state(ThreadStateKind::General, &buf).err(),
|
||||
Some(ZxError::BUFFER_TOO_SMALL)
|
||||
);
|
||||
|
||||
let mut buf = [0; SIZE];
|
||||
assert!(thread
|
||||
.read_state(ThreadStateKind::General, &mut buf)
|
||||
.is_ok());
|
||||
assert!(thread.write_state(ThreadStateKind::General, &buf).is_ok());
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn wait_for_run() {
|
||||
let root_job = Job::root();
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
||||
|
||||
assert_eq!(thread.state(), ThreadState::New);
|
||||
|
||||
thread
|
||||
.start(0, 0, 0, 0, |thread| Box::pin(new_thread(thread)))
|
||||
.unwrap();
|
||||
async fn new_thread(thread: CurrentThread) {
|
||||
assert_eq!(thread.state(), ThreadState::Running);
|
||||
|
||||
// without suspend
|
||||
let context = thread.wait_for_run().await;
|
||||
thread.end_running(context);
|
||||
|
||||
// with suspend
|
||||
thread.suspend();
|
||||
thread.suspend();
|
||||
assert_eq!(thread.state(), ThreadState::Suspended);
|
||||
async_std::task::spawn({
|
||||
let thread = (*thread).clone();
|
||||
async move {
|
||||
async_std::task::sleep(Duration::from_millis(10)).await;
|
||||
thread.resume();
|
||||
async_std::task::sleep(Duration::from_millis(10)).await;
|
||||
thread.resume();
|
||||
}
|
||||
});
|
||||
let time = timer_now();
|
||||
let _context = thread.wait_for_run().await;
|
||||
assert!(timer_now() - time >= Duration::from_millis(20));
|
||||
}
|
||||
// FIX ME: wait for thread end
|
||||
// let thread: Arc<dyn KernelObject> = thread;
|
||||
// thread.wait_signal(Signal::THREAD_TERMINATED).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn time() {
|
||||
let root_job = Job::root();
|
||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
||||
|
||||
assert_eq!(thread.get_time(), 0);
|
||||
thread.time_add(10);
|
||||
assert_eq!(thread.get_time(), 10);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
use crate::{ZxError, ZxResult};
|
||||
use kernel_hal::UserContext;
|
||||
use numeric_enum_macro::numeric_enum;
|
||||
|
||||
numeric_enum! {
|
||||
#[repr(u32)]
|
||||
/// Possible values for "kind" in zx_thread_read_state and zx_thread_write_state.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ThreadStateKind {
|
||||
General = 0,
|
||||
FS = 6,
|
||||
GS = 7,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) trait ContextExt {
|
||||
fn read_state(&self, kind: ThreadStateKind, buf: &mut [u8]) -> ZxResult<usize>;
|
||||
fn write_state(&mut self, kind: ThreadStateKind, buf: &[u8]) -> ZxResult;
|
||||
}
|
||||
|
||||
impl ContextExt for UserContext {
|
||||
fn read_state(&self, kind: ThreadStateKind, buf: &mut [u8]) -> ZxResult<usize> {
|
||||
match kind {
|
||||
ThreadStateKind::General => buf.write_struct(&self.general),
|
||||
ThreadStateKind::FS => buf.write_struct(&self.general.fsbase),
|
||||
ThreadStateKind::GS => buf.write_struct(&self.general.gsbase),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_state(&mut self, kind: ThreadStateKind, buf: &[u8]) -> ZxResult {
|
||||
match kind {
|
||||
ThreadStateKind::General => self.general = buf.read_struct()?,
|
||||
ThreadStateKind::FS => self.general.fsbase = buf.read_struct()?,
|
||||
ThreadStateKind::GS => self.general.gsbase = buf.read_struct()?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
trait BufExt {
|
||||
fn read_struct<T>(&self) -> ZxResult<T>;
|
||||
fn write_struct<T: Copy>(&mut self, value: &T) -> ZxResult<usize>;
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
impl BufExt for [u8] {
|
||||
fn read_struct<T>(&self) -> ZxResult<T> {
|
||||
if self.len() < core::mem::size_of::<T>() {
|
||||
return Err(ZxError::BUFFER_TOO_SMALL);
|
||||
}
|
||||
Ok(unsafe { (self.as_ptr() as *const T).read() })
|
||||
}
|
||||
|
||||
fn write_struct<T: Copy>(&mut self, value: &T) -> ZxResult<usize> {
|
||||
if self.len() < core::mem::size_of::<T>() {
|
||||
return Err(ZxError::BUFFER_TOO_SMALL);
|
||||
}
|
||||
unsafe {
|
||||
*(self.as_mut_ptr() as *mut T) = *value;
|
||||
}
|
||||
Ok(core::mem::size_of::<T>())
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
//! Objects for Virtual Memory Management.
|
||||
use super::*;
|
||||
|
||||
mod vmar;
|
||||
|
||||
pub use self::vmar::*;
|
||||
@ -0,0 +1,17 @@
|
||||
use {super::*, crate::object::*, alloc::sync::Arc};
|
||||
|
||||
/// Virtual Memory Address Regions
|
||||
pub struct VmAddressRegion {
|
||||
base: KObjectBase,
|
||||
}
|
||||
|
||||
impl_kobject!(VmAddressRegion);
|
||||
|
||||
impl VmAddressRegion {
|
||||
/// Create a new root VMAR.
|
||||
pub fn new_root() -> Arc<Self> {
|
||||
Arc::new(VmAddressRegion {
|
||||
base: KObjectBase::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue