commit
a06aeb685b
@ -0,0 +1,2 @@
|
||||
target
|
||||
Cargo.lock
|
||||
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "ch01-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"
|
||||
buddy_system_allocator = "0.7"
|
||||
@ -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,180 @@
|
||||
use {
|
||||
super::*,
|
||||
crate::error::*,
|
||||
crate::object::*,
|
||||
alloc::collections::VecDeque,
|
||||
alloc::sync::{Arc, Weak},
|
||||
alloc::vec::Vec,
|
||||
core::convert::TryInto,
|
||||
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(msg) = 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::*;
|
||||
use alloc::boxed::Box;
|
||||
use core::sync::atomic::*;
|
||||
use core::time::Duration;
|
||||
|
||||
#[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,9 @@
|
||||
#![no_std]
|
||||
#![feature(get_mut_unchecked)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod error;
|
||||
mod ipc;
|
||||
mod object;
|
||||
mod task;
|
||||
@ -0,0 +1,18 @@
|
||||
// ANCHOR: handle
|
||||
use super::{KernelObject, Rights};
|
||||
use alloc::sync::Arc;
|
||||
|
||||
/// 内核对象句柄
|
||||
#[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,35 @@
|
||||
// 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;
|
||||
|
||||
const DEFAULT_CHANNEL = Self::BASIC.bits & !Self::DUPLICATE.bits | Self::IO.bits | Self::SIGNAL.bits | Self::SIGNAL_PEER.bits;
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: rights
|
||||
@ -0,0 +1,3 @@
|
||||
use super::*;
|
||||
|
||||
mod process;
|
||||
@ -0,0 +1,77 @@
|
||||
use super::*;
|
||||
use crate::error::*;
|
||||
use crate::object::*;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::sync::Arc;
|
||||
use spin::Mutex;
|
||||
|
||||
// ANCHOR: process
|
||||
/// 进程对象
|
||||
pub struct Process {
|
||||
base: KObjectBase,
|
||||
inner: Mutex<ProcessInner>,
|
||||
}
|
||||
|
||||
impl_kobject!(Process);
|
||||
|
||||
struct ProcessInner {
|
||||
handles: BTreeMap<HandleValue, Handle>,
|
||||
}
|
||||
|
||||
pub type HandleValue = u32;
|
||||
|
||||
impl Process {
|
||||
/// 创建一个新的进程对象
|
||||
pub fn new() -> Arc<Self> {
|
||||
Arc::new(Process {
|
||||
base: KObjectBase::default(),
|
||||
inner: Mutex::new(ProcessInner {
|
||||
handles: BTreeMap::default(),
|
||||
}),
|
||||
})
|
||||
}
|
||||
// ANCHOR_END: process
|
||||
|
||||
// ANCHOR: add_remove_handle
|
||||
/// 添加一个新的对象句柄
|
||||
pub fn add_handle(&self, handle: Handle) -> HandleValue {
|
||||
let mut inner = self.inner.lock();
|
||||
let value = (0 as HandleValue..)
|
||||
.find(|idx| !inner.handles.contains_key(idx))
|
||||
.unwrap();
|
||||
inner.handles.insert(value, handle);
|
||||
value
|
||||
}
|
||||
|
||||
/// 删除一个对象句柄
|
||||
pub fn remove_handle(&self, handle_value: HandleValue) {
|
||||
self.inner.lock().handles.remove(&handle_value);
|
||||
}
|
||||
// ANCHOR_END: add_remove_handle
|
||||
|
||||
// ANCHOR: get_object_with_rights
|
||||
/// 根据句柄值查找内核对象,并检查权限
|
||||
pub fn get_object_with_rights<T: KernelObject>(
|
||||
&self,
|
||||
handle_value: HandleValue,
|
||||
desired_rights: Rights,
|
||||
) -> ZxResult<Arc<T>> {
|
||||
let handle = self
|
||||
.inner
|
||||
.lock()
|
||||
.handles
|
||||
.get(&handle_value)
|
||||
.ok_or(ZxError::BAD_HANDLE)?
|
||||
.clone();
|
||||
// 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)
|
||||
}
|
||||
// ANCHOR_END: get_object_with_rights
|
||||
}
|
||||
Loading…
Reference in new issue