ch02-03, basic code

master
DeathWish5 5 years ago
parent fe4ee39b03
commit cf54a7b958

@ -4,4 +4,5 @@ members = [
"ch01-02",
"ch01-03",
"ch02-02",
"ch02-03",
]

@ -163,7 +163,15 @@ impl Process {
/// The process finally terminates.
fn terminate(&self) {
// unimplemented!()
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.
@ -261,11 +269,17 @@ impl Task for Process {
}
fn suspend(&self) {
// unimplemented!()
// let inner = self.inner.lock();
// for thread in inner.threads.iter() {
// thread.suspend();
// }
}
fn resume(&self) {
// unimplemented!()
// let inner = self.inner.lock();
// for thread in inner.threads.iter() {
// thread.resume();
// }
}
}

@ -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,185 @@
use alloc::string::String;
use alloc::sync::Arc;
use core::fmt::Debug;
use core::sync::atomic::*;
use downcast_rs::{impl_downcast, DowncastSync};
use spin::Mutex;
mod handle;
mod rights;
pub use self::handle::*;
pub use self::rights::*;
pub use super::*;
/// 内核对象公共接口
pub trait KernelObject: DowncastSync + Debug {
/// 获取对象 ID
fn id(&self) -> KoID;
/// 获取对象类型名
fn type_name(&self) -> &str;
/// 获取对象名称
fn name(&self) -> String;
/// 设置对象名称
fn set_name(&self, name: &str);
/// 尝试获取对象伙伴
///
/// 当前该对象必须是 `Channel`
fn peer(&self) -> ZxResult<Arc<dyn KernelObject>> {
Err(ZxError::NOT_SUPPORTED)
}
/// 尝试获取关联对象 id否则返回 0
///
/// 当前该对象必须是 `Channel` 或者 `Task`
///
/// 如果该对象是 `Channel`, 将获取伙伴的 id
///
/// 如果该对象是 `Task`, 将获取其父 `Task` 的 id
fn related_koid(&self) -> KoID {
0
}
/// 尝试获取对应 id 的子对象
///
/// 当前该对象必须是 `Job` 或者 `Process`.
///
/// 如果该对象是 `Job`,则其直属子 `Job` 以及 `Process` 必须被获取。
fn get_child(&self, _id: KoID) -> ZxResult<Arc<dyn KernelObject>> {
Err(ZxError::WRONG_TYPE)
}
}
impl_downcast!(sync KernelObject);
/// 对象 ID 类型
pub type KoID = u64;
/// 内核对象核心结构
pub struct KObjectBase {
/// 对象 ID
pub id: KoID,
inner: Mutex<KObjectBaseInner>,
}
/// `KObjectBase` 的内部可变部分
#[derive(Default)]
struct KObjectBaseInner {
name: String,
}
impl Default for KObjectBase {
/// 创建一个新 `KObjectBase`
fn default() -> Self {
KObjectBase {
id: Self::new_koid(),
inner: Default::default(),
}
}
}
impl KObjectBase {
/// Create a new kernel object base.
pub fn new() -> Self {
Self::default()
}
/// 生成一个唯一的 ID
fn new_koid() -> KoID {
static NEXT_KOID: AtomicU64 = AtomicU64::new(1024);
NEXT_KOID.fetch_add(1, Ordering::SeqCst)
}
/// 获取对象名称
pub fn name(&self) -> String {
self.inner.lock().name.clone()
}
/// 设置对象名称
pub fn set_name(&self, name: &str) {
self.inner.lock().name = String::from(name);
}
/// Create a kernel object base with `name`.
pub fn with_name(name: &str) -> Self {
KObjectBase {
id: Self::new_koid(),
inner: Mutex::new(KObjectBaseInner {
name: String::from(name),
}),
}
}
}
/// 为内核对象 struct 自动实现 `KernelObject` trait 的宏。
#[macro_export] // 导出宏,可在 crate 外部使用
macro_rules! impl_kobject {
// 匹配类型名,并可以提供函数覆盖默认实现
($class:ident $( $fn:tt )*) => {
// 为对象实现 KernelObject trait方法直接转发到内部 struct
impl KernelObject for $class {
fn id(&self) -> KoID {
// 直接访问内部的 pub 属性
self.base.id
}
fn type_name(&self) -> &str {
// 用 stringify! 宏将输入转成字符串
stringify!($class)
}
// 注意宏里面的类型要写完整路径例如alloc::string::String
fn name(&self) -> alloc::string::String {
self.base.name()
}
fn set_name(&self, name: &str){
// 直接访问内部的 pub 方法
self.base.set_name(name)
}
// 可以传入任意数量的函数,覆盖 trait 的默认实现
$( $fn )*
}
// 为对象实现 Debug trait
impl core::fmt::Debug for $class {
fn fmt(
&self,
f: &mut core::fmt::Formatter<'_>,
) -> core::result::Result<(), core::fmt::Error> {
// 输出对象类型、ID 和名称
f.debug_tuple(&stringify!($class))
.field(&self.id())
.field(&self.name())
.finish()
}
}
};
}
/// 空对象
pub struct DummyObject {
// 其中必须包含一个名为 `base` 的 `KObjectBase`
base: KObjectBase,
}
// 使用刚才的宏,声明其为内核对象,自动生成必要的代码
impl_kobject!(DummyObject);
impl DummyObject {
/// 创建一个新 `DummyObject`
pub fn new() -> Arc<Self> {
Arc::new(DummyObject {
base: KObjectBase::default(),
})
}
}
#[cfg(test)]
#[test]
fn impl_kobject() {
use alloc::format;
let dummy = DummyObject::new();
let object: Arc<dyn KernelObject> = dummy;
assert_eq!(object.type_name(), "DummyObject");
assert_eq!(object.name(), "");
object.set_name("dummy");
assert_eq!(object.name(), "dummy");
assert_eq!(
format!("{:?}", object),
format!("DummyObject({}, \"dummy\")", object.id())
);
let _result: Arc<DummyObject> = object.downcast_arc::<DummyObject>().unwrap();
}

@ -0,0 +1,57 @@
// ANCHOR: rights
use bitflags::bitflags;
bitflags! {
/// 句柄权限
pub struct Rights: u32 {
const DUPLICATE = 1 << 0;
const TRANSFER = 1 << 1;
const READ = 1 << 2;
const WRITE = 1 << 3;
const EXECUTE = 1 << 4;
const MAP = 1 << 5;
const GET_PROPERTY = 1 << 6;
const SET_PROPERTY = 1 << 7;
const ENUMERATE = 1 << 8;
const DESTROY = 1 << 9;
const SET_POLICY = 1 << 10;
const GET_POLICY = 1 << 11;
const SIGNAL = 1 << 12;
const SIGNAL_PEER = 1 << 13;
const WAIT = 1 << 14;
const INSPECT = 1 << 15;
const MANAGE_JOB = 1 << 16;
const MANAGE_PROCESS = 1 << 17;
const MANAGE_THREAD = 1 << 18;
const APPLY_PROFILE = 1 << 19;
const SAME_RIGHTS = 1 << 31;
const BASIC = Self::TRANSFER.bits | Self::DUPLICATE.bits | Self::WAIT.bits | Self::INSPECT.bits;
const IO = Self::READ.bits | Self::WRITE.bits;
/// GET_PROPERTY SET_PROPERTY
const PROPERTY = Self::GET_PROPERTY.bits | Self::SET_PROPERTY.bits;
/// GET_POLICY SET_POLICY
const POLICY = Self::GET_POLICY.bits | Self::SET_POLICY.bits;
/// BASIC & !Self::DUPLICATE | IO | SIGNAL | SIGNAL_PEER
const DEFAULT_CHANNEL = Self::BASIC.bits & !Self::DUPLICATE.bits | Self::IO.bits | Self::SIGNAL.bits | Self::SIGNAL_PEER.bits;
/// BASIC | IO | PROPERTY | ENUMERATE | DESTROY | SIGNAL | MANAGE_PROCESS | MANAGE_THREAD
const DEFAULT_PROCESS = Self::BASIC.bits | Self::IO.bits | Self::PROPERTY.bits | Self::ENUMERATE.bits | Self::DESTROY.bits
| Self::SIGNAL.bits | Self::MANAGE_PROCESS.bits | Self::MANAGE_THREAD.bits;
/// BASIC | IO | PROPERTY | DESTROY | SIGNAL | MANAGE_THREAD
const DEFAULT_THREAD = Self::BASIC.bits | Self::IO.bits | Self::PROPERTY.bits | Self::DESTROY.bits | Self::SIGNAL.bits | Self::MANAGE_THREAD.bits;
/// BASIC | WAIT
const DEFAULT_VMAR = Self::BASIC.bits & !Self::WAIT.bits;
/// BASIC | IO | PROPERTY | POLICY | ENUMERATE | DESTROY | SIGNAL | MANAGE_JOB | MANAGE_PROCESS | MANAGE_THREAD
const DEFAULT_JOB = Self::BASIC.bits | Self::IO.bits | Self::PROPERTY.bits | Self::POLICY.bits | Self::ENUMERATE.bits
| Self::DESTROY.bits | Self::SIGNAL.bits | Self::MANAGE_JOB.bits | Self::MANAGE_PROCESS.bits | Self::MANAGE_THREAD.bits;
}
}
// ANCHOR_END: rights

@ -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…
Cancel
Save