diff --git a/kernel-hal-bare/src/arch/x86_64/interrupt.rs b/kernel-hal-bare/src/arch/x86_64/interrupt.rs index 4cd8fa934..0c1a9acf1 100644 --- a/kernel-hal-bare/src/arch/x86_64/interrupt.rs +++ b/kernel-hal-bare/src/arch/x86_64/interrupt.rs @@ -1,12 +1,32 @@ #![allow(dead_code)] #![allow(non_upper_case_globals)] + +use alloc::sync::Arc; +use alloc::vec::Vec; +use spin::Mutex; use trapframe::TrapFrame; +pub type InterruptHandle = Arc; +lazy_static! { + static ref IRQ_TABLE: Mutex>> = Default::default(); +} + pub fn init() { + init_irq_table(); + irq_add_handle(Timer, Arc::new(timer)); + irq_add_handle(COM1, Arc::new(com1)); + irq_add_handle(Keyboard, Arc::new(keyboard)); super::irq_enable(Keyboard); super::irq_enable(COM1); } +fn init_irq_table() { + let mut table = IRQ_TABLE.lock(); + for _ in 0..64 { + table.push(None); + } +} + #[no_mangle] pub extern "C" fn trap_handler(tf: &mut TrapFrame) { trace!("Interrupt: {:#x} @ CPU{}", tf.trap_num, 0); // TODO 0 should replace in multi-core case @@ -24,11 +44,38 @@ pub fn irq_handle(irq: u8) { use super::{phys_to_virt, LocalApic, XApic, LAPIC_ADDR}; let mut lapic = unsafe { XApic::new(phys_to_virt(LAPIC_ADDR)) }; lapic.eoi(); - match irq { - Timer => timer(), - COM1 => com1(), - Keyboard => keyboard(), - _ => panic!("unhandled external IRQ number: {}", irq), + let table = IRQ_TABLE.lock(); + match &table[irq as usize] { + Some(f) => f(), + None => panic!("unhandled external IRQ number: {}", irq), + } +} + +#[export_name = "hal_irq_add_handle"] +pub fn irq_add_handle(irq: u8, handle: InterruptHandle) -> bool { + info!("IRQ add handle {}", irq); + let irq = irq as usize; + let mut table = IRQ_TABLE.lock(); + match table[irq] { + Some(_) => false, + None => { + table[irq] = Some(handle); + true + } + } +} + +#[export_name = "hal_irq_remove_handle"] +pub fn irq_remove_handle(irq: u8) -> bool { + info!("IRQ remove handle {}", irq); + let irq = irq as usize; + let mut table = IRQ_TABLE.lock(); + match table[irq] { + Some(_) => { + table[irq] = None; + false + } + None => true, } } diff --git a/kernel-hal-bare/src/arch/x86_64/mod.rs b/kernel-hal-bare/src/arch/x86_64/mod.rs index 41bdb2e3c..c567063f6 100644 --- a/kernel-hal-bare/src/arch/x86_64/mod.rs +++ b/kernel-hal-bare/src/arch/x86_64/mod.rs @@ -310,12 +310,18 @@ fn timer_init() { lapic.cpu_init(); } -#[inline(always)] +#[export_name = "hal_irq_enable"] pub fn irq_enable(irq: u8) { let mut ioapic = unsafe { IoApic::new(phys_to_virt(IOAPIC_ADDR)) }; ioapic.enable(irq, 0); } +#[export_name = "hal_irq_disable"] +pub fn irq_disable(irq: u8) { + let mut ioapic = unsafe { IoApic::new(phys_to_virt(IOAPIC_ADDR)) }; + ioapic.disable(irq); +} + const LAPIC_ADDR: usize = 0xfee0_0000; const IOAPIC_ADDR: usize = 0xfec0_0000; diff --git a/kernel-hal/src/dummy.rs b/kernel-hal/src/dummy.rs index 6007f65c9..798358a5e 100644 --- a/kernel-hal/src/dummy.rs +++ b/kernel-hal/src/dummy.rs @@ -1,6 +1,7 @@ use super::*; use crate::vdso::VdsoConstants; use alloc::boxed::Box; +use alloc::sync::Arc; use alloc::vec::Vec; use core::future::Future; use core::ops::FnOnce; @@ -265,6 +266,34 @@ pub fn irq_handle(_irq: u8) { unimplemented!() } +/// Add an interrupt handle to an irq +#[linkage = "weak"] +#[export_name = "hal_irq_add_handle"] +pub fn irq_add_handle(_irq: u8, _handle: Arc) -> bool { + unimplemented!() +} + +/// Remove the interrupt handle of an irq +#[linkage = "weak"] +#[export_name = "hal_irq_remove_handle"] +pub fn irq_remove_handle(_irq: u8) -> bool { + unimplemented!() +} + +/// Enable IRQ. +#[linkage = "weak"] +#[export_name = "hal_irq_enable"] +pub fn irq_enable(_irq: u8) { + unimplemented!() +} + +/// Disable IRQ. +#[linkage = "weak"] +#[export_name = "hal_irq_disable"] +pub fn irq_disable(_irq: u8) { + unimplemented!() +} + /// Get platform specific information. #[linkage = "weak"] #[export_name = "hal_vdso_constants"] diff --git a/zircon-object/src/dev/interrupt/event_interrupt.rs b/zircon-object/src/dev/interrupt/event_interrupt.rs new file mode 100644 index 000000000..0d45314ca --- /dev/null +++ b/zircon-object/src/dev/interrupt/event_interrupt.rs @@ -0,0 +1,57 @@ +use {super::*, alloc::sync::Arc, spin::Mutex}; + +pub struct EventInterrupt { + vector: u8, + inner: Mutex, +} + +#[derive(Default)] +struct EventInterruptInner { + register: bool, +} + +impl EventInterrupt { + pub fn new(vector: usize) -> Arc { + // TODO check vector is a vaild IRQ number + Arc::new(EventInterrupt { + vector: vector as u8, + inner: Default::default(), + }) + } +} + +impl InterruptTrait for EventInterrupt { + fn mask_interrupt_locked(&self) { + kernel_hal::irq_disable(self.vector as u8); + } + + fn unmask_interrupt_locked(&self) { + kernel_hal::irq_enable(self.vector as u8); + } + + fn register_interrupt_handler(&self, handle: Arc) -> ZxResult { + let mut inner = self.inner.lock(); + if inner.register { + return Err(ZxError::ALREADY_BOUND); + } + if kernel_hal::irq_add_handle(self.vector, handle) { + inner.register = true; + Ok(()) + } else { + Err(ZxError::ALREADY_BOUND) + } + } + + fn unregister_interrupt_handler(&self) -> ZxResult { + let mut inner = self.inner.lock(); + if !inner.register { + return Ok(()); + } + if kernel_hal::irq_remove_handle(self.vector) { + inner.register = false; + Ok(()) + } else { + Err(ZxError::ALREADY_BOUND) + } // maybe a better error code? + } +} diff --git a/zircon-object/src/dev/interrupt/mod.rs b/zircon-object/src/dev/interrupt/mod.rs new file mode 100644 index 000000000..fd0094547 --- /dev/null +++ b/zircon-object/src/dev/interrupt/mod.rs @@ -0,0 +1,327 @@ +use { + self::event_interrupt::*, self::virtual_interrupt::*, crate::object::*, crate::signal::*, + alloc::sync::Arc, bitflags::bitflags, spin::Mutex, +}; + +mod event_interrupt; +mod virtual_interrupt; + +pub trait InterruptTrait: Sync + Send { + fn mask_interrupt_locked(&self); + fn unmask_interrupt_locked(&self); + fn register_interrupt_handler(&self, handle: Arc) -> ZxResult; + fn unregister_interrupt_handler(&self) -> ZxResult; +} + +impl_kobject!(Interrupt); + +pub struct Interrupt { + base: KObjectBase, + hasvcpu: bool, + flags: InterruptFlags, + inner: Mutex, + trait_: Arc, +} + +#[derive(Default)] +struct InterruptInner { + state: InterruptState, + port: Option>, + key: u64, + timestamp: i64, + defer_unmask: bool, + packet_id: u64, +} + +impl Drop for Interrupt { + fn drop(&mut self) { + self.destroy().unwrap(); + } +} + +impl Interrupt { + pub fn new_virtual(options: InterruptOptions) -> ZxResult> { + if options != InterruptOptions::VIRTUAL { + return Err(ZxError::INVALID_ARGS); + } + Ok(Arc::new(Interrupt { + base: KObjectBase::new(), + hasvcpu: false, + flags: InterruptFlags::VIRTUAL, + inner: Default::default(), + trait_: VirtualInterrupt::new(), + })) + } + + pub fn new_event(mut vector: usize, options: InterruptOptions) -> ZxResult> { + let mode = options.to_mode(); + if mode != InterruptOptions::MODE_DEFAULT && mode != InterruptOptions::MODE_EDGE_HIGH { + unimplemented!(); + } + // I don't know the real mapping, +16 only to avoid conflict + if options.contains(InterruptOptions::REMAP_IRQ) { + vector += 16; + // vector = EventInterrupt::remap(vector); + } + let event_interrupt = Arc::new(Interrupt { + base: KObjectBase::new(), + hasvcpu: false, + flags: InterruptFlags::empty(), + inner: Default::default(), + trait_: EventInterrupt::new(vector), + }); + let event_interrupt_clone = event_interrupt.clone(); + event_interrupt + .trait_ + .register_interrupt_handler(Arc::new(move || { + event_interrupt_clone.interrupt_handle() + }))?; + event_interrupt.trait_.unmask_interrupt_locked(); + Ok(event_interrupt) + } + + pub fn bind(&self, port: Arc, key: u64) -> ZxResult { + let mut inner = self.inner.lock(); + match inner.state { + InterruptState::DESTORY => return Err(ZxError::CANCELED), + InterruptState::WAITING => return Err(ZxError::BAD_STATE), + _ => (), + } + if inner.port.is_some() || self.hasvcpu { + return Err(ZxError::ALREADY_BOUND); + } + if self + .flags + .contains(InterruptFlags::UNMASK_PREWAIT_UNLOCKED | InterruptFlags::MASK_POSTWAIT) + { + return Err(ZxError::INVALID_ARGS); + } + inner.port = Some(port.clone()); + inner.key = key; + if inner.state == InterruptState::TRIGGERED { + inner.packet_id = port.as_ref().push_interrupt(inner.timestamp, inner.key); + inner.state = InterruptState::NEEDACK; + } + Ok(()) + } + + pub fn unbind(&self, port: Arc) -> ZxResult { + let mut inner = self.inner.lock(); + if inner.port.is_none() || inner.port.as_ref().unwrap().id() != port.id() { + return Err(ZxError::NOT_FOUND); + } + if inner.state == InterruptState::DESTORY { + return Err(ZxError::CANCELED); + } + port.remove_interrupt(inner.packet_id); + inner.port = None; + inner.key = 0; + Ok(()) + } + + pub fn trigger(&self, timestamp: i64) -> ZxResult { + if !self.flags.contains(InterruptFlags::VIRTUAL) { + return Err(ZxError::BAD_STATE); + } + let mut inner = self.inner.lock(); + if inner.timestamp == 0 { + inner.timestamp = timestamp; + } + if inner.state == InterruptState::DESTORY { + return Err(ZxError::CANCELED); + } + if inner.state == InterruptState::NEEDACK && inner.port.is_some() { + return Ok(()); + } + if let Some(port) = &inner.port { + // TODO: use a function to send the package + inner.packet_id = port.as_ref().push_interrupt(timestamp, inner.key); + if self.flags.contains(InterruptFlags::MASK_POSTWAIT) { + self.trait_.mask_interrupt_locked(); + } + inner.timestamp = 0; + inner.state = InterruptState::NEEDACK; + } else { + inner.state = InterruptState::TRIGGERED; + self.base.signal_set(Signal::INTERRUPT_SIGNAL); + } + Ok(()) + } + + pub fn ack(&self) -> ZxResult { + let mut inner = self.inner.lock(); + if inner.port.is_none() { + return Err(ZxError::BAD_STATE); + } + if inner.state == InterruptState::DESTORY { + return Err(ZxError::CANCELED); + } + if inner.state == InterruptState::NEEDACK { + if self.flags.contains(InterruptFlags::UNMASK_PREWAIT) { + self.trait_.unmask_interrupt_locked(); + } else if self.flags.contains(InterruptFlags::UNMASK_PREWAIT_UNLOCKED) { + inner.defer_unmask = true; + } + if inner.timestamp > 0 { + // TODO: use a function to send the package + inner.packet_id = inner + .port + .as_ref() + .unwrap() + .as_ref() + .push_interrupt(inner.timestamp, inner.key); + if self.flags.contains(InterruptFlags::MASK_POSTWAIT) { + self.trait_.mask_interrupt_locked(); + } + inner.timestamp = 0; + } else { + inner.state = InterruptState::IDLE; + } + } + if inner.defer_unmask { + self.trait_.unmask_interrupt_locked(); + } + Ok(()) + } + + pub fn destroy(&self) -> ZxResult { + self.trait_.mask_interrupt_locked(); + self.trait_.unregister_interrupt_handler()?; + let mut inner = self.inner.lock(); + if let Some(port) = &inner.port { + let in_queue = port.remove_interrupt(inner.packet_id); + match inner.state { + InterruptState::NEEDACK => { + inner.state = InterruptState::DESTORY; + if !in_queue { + Err(ZxError::NOT_FOUND) + } else { + Ok(()) + } + } + + InterruptState::IDLE => { + inner.state = InterruptState::DESTORY; + Ok(()) + } + + _ => Ok(()), + } + } else { + inner.state = InterruptState::DESTORY; + self.base.signal_set(Signal::INTERRUPT_SIGNAL); + Ok(()) + } + } + + pub async fn wait(self: &Arc) -> ZxResult { + let mut defer_unmask = false; + let object = self.clone() as Arc; + loop { + { + let mut inner = self.inner.lock(); + if inner.port.is_some() || self.hasvcpu { + return Err(ZxError::BAD_STATE); + } + match inner.state { + InterruptState::DESTORY => return Err(ZxError::CANCELED), + InterruptState::TRIGGERED => { + inner.state = InterruptState::TRIGGERED; + let timestamp = inner.timestamp; + inner.timestamp = 0; + self.base.signal_clear(Signal::INTERRUPT_SIGNAL); + return Ok(timestamp); + } + InterruptState::NEEDACK => { + if self.flags.contains(InterruptFlags::UNMASK_PREWAIT) { + self.trait_.unmask_interrupt_locked(); + } else if self.flags.contains(InterruptFlags::UNMASK_PREWAIT_UNLOCKED) { + defer_unmask = true; + } + } + InterruptState::IDLE => (), + _ => return Err(ZxError::BAD_STATE), + } + inner.state = InterruptState::WAITING; + } + if defer_unmask { + self.trait_.unmask_interrupt_locked(); + } + object.wait_signal(Signal::INTERRUPT_SIGNAL).await; + } + } + + pub fn interrupt_handle(&self) { + let mut inner = self.inner.lock(); + if self.flags.contains(InterruptFlags::MASK_POSTWAIT) { + self.trait_.mask_interrupt_locked(); + } + if inner.timestamp == 0 { + // Not sure ZX_CLOCK_MONOTONIC or ZX_CLOCK_UTC + inner.timestamp = kernel_hal::timer_now().as_nanos() as i64; + } + match &inner.port { + Some(port) => { + if inner.state != InterruptState::NEEDACK { + // TODO: use a function to send the package + inner.packet_id = port.as_ref().push_interrupt(inner.timestamp, inner.key); + if self.flags.contains(InterruptFlags::MASK_POSTWAIT) { + self.trait_.mask_interrupt_locked(); + } + inner.timestamp = 0; + + inner.state = InterruptState::NEEDACK; + } + } + None => { + self.base.signal_set(Signal::INTERRUPT_SIGNAL); + inner.state = InterruptState::TRIGGERED; + } + } + } +} + +#[derive(PartialEq, Debug)] +enum InterruptState { + WAITING = 0, + DESTORY = 1, + TRIGGERED = 2, + NEEDACK = 3, + IDLE = 4, +} + +impl Default for InterruptState { + fn default() -> Self { + InterruptState::IDLE + } +} + +bitflags! { + pub struct InterruptFlags: u32 { + #[allow(clippy::identity_op)] + const VIRTUAL = 1 << 0; + const UNMASK_PREWAIT = 1 << 1; + const UNMASK_PREWAIT_UNLOCKED = 1 << 2; + const MASK_POSTWAIT = 1 << 4; + } +} + +bitflags! { + pub struct InterruptOptions: u32 { + #[allow(clippy::identity_op)] + const REMAP_IRQ = 0x1; + const MODE_DEFAULT = 0 << 1; + const MODE_EDGE_LOW = 1 << 1; + const MODE_EDGE_HIGH = 2 << 1; + const MODE_LEVEL_LOW = 3 << 1; + const MODE_LEVEL_HIGH = 4 << 1; + const MODE_EDGE_BOTH = 5 << 1; + const VIRTUAL = 0x10; + } +} + +impl InterruptOptions { + pub fn to_mode(self) -> Self { + InterruptOptions::from_bits_truncate(0xe) & self + } +} diff --git a/zircon-object/src/dev/interrupt/virtual_interrupt.rs b/zircon-object/src/dev/interrupt/virtual_interrupt.rs new file mode 100644 index 000000000..087c23120 --- /dev/null +++ b/zircon-object/src/dev/interrupt/virtual_interrupt.rs @@ -0,0 +1,21 @@ +use {super::*, alloc::sync::Arc}; + +#[derive(Default)] +pub struct VirtualInterrupt {} + +impl VirtualInterrupt { + pub fn new() -> Arc { + Default::default() + } +} + +impl InterruptTrait for VirtualInterrupt { + fn mask_interrupt_locked(&self) {} + fn unmask_interrupt_locked(&self) {} + fn register_interrupt_handler(&self, _handle: Arc) -> ZxResult { + Ok(()) + } + fn unregister_interrupt_handler(&self) -> ZxResult { + Ok(()) + } +} diff --git a/zircon-object/src/dev/mod.rs b/zircon-object/src/dev/mod.rs index f0a433749..298ff9046 100644 --- a/zircon-object/src/dev/mod.rs +++ b/zircon-object/src/dev/mod.rs @@ -1,8 +1,9 @@ use super::*; mod bti; +mod interrupt; mod iommu; mod pmt; mod resource; -pub use self::{bti::*, iommu::*, pmt::*, resource::*}; +pub use self::{bti::*, interrupt::*, iommu::*, pmt::*, resource::*}; diff --git a/zircon-object/src/dev/resource.rs b/zircon-object/src/dev/resource.rs index cefda1962..e03f3d780 100644 --- a/zircon-object/src/dev/resource.rs +++ b/zircon-object/src/dev/resource.rs @@ -84,4 +84,28 @@ impl Resource { Ok(()) } } + + pub fn get_info(&self) -> ResourceInfo { + let name = self.base.name(); + let name = name.as_bytes(); + let mut name_vec = [0 as u8; 32]; + name_vec[..name.len()].clone_from_slice(name); + ResourceInfo { + kind: self.kind as _, + flags: self.flags.bits, + base: self.addr as _, + size: self.len as _, + name: name_vec, + } + } +} + +#[repr(C)] +#[derive(Default)] +pub struct ResourceInfo { + kind: u32, + flags: u32, + base: u32, + size: u64, + name: [u8; 32], // should be [char; 32], but I cannot compile it } diff --git a/zircon-object/src/ipc/socket.rs b/zircon-object/src/ipc/socket.rs index 9d7dfd379..a7eee64a5 100644 --- a/zircon-object/src/ipc/socket.rs +++ b/zircon-object/src/ipc/socket.rs @@ -1,11 +1,26 @@ use { crate::object::*, alloc::sync::{Arc, Weak}, + alloc::vec::Vec, + bitflags::bitflags, + core::cmp::min, + core::iter::FromIterator, + spin::Mutex, }; pub struct Socket { base: KObjectBase, peer: Weak, + lock: Arc>, + inner: Mutex, +} + +#[derive(Default)] +struct SocketInner { + read_disabled: bool, + read_threshold: usize, + write_threshold: usize, // only for core-test + data: Vec, } impl_kobject!(Socket @@ -18,16 +33,23 @@ impl_kobject!(Socket } ); +// Only support stream mode +// The size of data is unlimited impl Socket { #[allow(unsafe_code)] pub fn create() -> (Arc, Arc) { + let lock = Arc::new(Mutex::new(0 as u8)); let mut end0 = Arc::new(Socket { base: KObjectBase::with_signal(Signal::WRITABLE), peer: Weak::default(), + lock: lock.clone(), + inner: Default::default(), }); let end1 = Arc::new(Socket { base: KObjectBase::with_signal(Signal::WRITABLE), peer: Arc::downgrade(&end0), + lock, + inner: Default::default(), }); // no other reference of `end0` unsafe { @@ -35,4 +57,167 @@ impl Socket { } (end0, end1) } + + pub fn read(&self, size: usize, options: SocketOptions) -> ZxResult> { + let _ = self.lock.lock(); + let mut inner = self.inner.lock(); + if inner.data.is_empty() { + let peer = self.peer.upgrade(); + if peer.is_none() { + return Err(ZxError::PEER_CLOSED); + } + if inner.read_disabled { + return Err(ZxError::BAD_STATE); + } + return Err(ZxError::SHOULD_WAIT); + } + let size = min(size, inner.data.len()); + let data = if options.contains(SocketOptions::PEEK) { + Vec::from_iter(inner.data[0..size].iter().cloned()) + } else { + inner.data.drain(..size).collect::>() + }; + let mut clear = Signal::empty(); + if inner.read_threshold > 0 && inner.data.len() < inner.read_threshold { + clear |= Signal::SOCKET_READ_THRESHOLD; + } + if inner.data.is_empty() { + clear |= Signal::READABLE; + } + self.signal_change(clear, Signal::empty()); + Ok(data) + } + + pub fn write(&self, mut buffer: Vec) -> ZxResult { + let _ = self.lock.lock(); + let peer = self.peer.upgrade(); + if peer.is_none() { + return Err(ZxError::PEER_CLOSED); + } + let peer = peer.unwrap(); + if self.signal().contains(Signal::SOCKET_WRITE_DISABLED) { + return Err(ZxError::BAD_STATE); + } + if buffer.is_empty() { + return Ok(0); + } + let buffer_len = buffer.len(); + let mut peer_inner = peer.inner.lock(); + let mut set = Signal::empty(); + if peer_inner.data.is_empty() { + set |= Signal::READABLE; + } + peer_inner.data.append(&mut buffer); + if peer_inner.read_threshold > 0 && peer_inner.data.len() >= peer_inner.read_threshold { + set |= Signal::SOCKET_READ_THRESHOLD; + } + peer.signal_change(Signal::empty(), set); + Ok(buffer_len) + } + + pub fn get_info(&self) -> SocketInfo { + let _ = self.lock.lock(); + let self_size = self.inner.lock().data.len(); + let peer_size = match self.peer.upgrade() { + Some(peer) => peer.inner.lock().data.len(), + None => 0, + }; + SocketInfo { + options: 0, + padding1: 0, + rx_buf_max: u64::MAX, + rx_buf_size: self_size as _, + rx_buf_available: self_size as _, + tx_buf_max: u64::MAX, + tx_buf_size: peer_size as _, + } + } + + pub fn shutdown(&self, options: SocketOptions) -> ZxResult { + let _ = self.lock.lock(); + self.shutdown_self(options)?; + if let Some(peer) = self.peer.upgrade() { + peer.shutdown_self( + options ^ SocketOptions::SHUTDOWN_READ ^ SocketOptions::SHUTDOWN_WRITE, + )?; + } + Ok(()) + } + + pub fn shutdown_self(&self, options: SocketOptions) -> ZxResult { + let mut set = Signal::empty(); + let mut clear = Signal::empty(); + let mut inner = self.inner.lock(); + if options.contains(SocketOptions::SHUTDOWN_READ) { + inner.read_disabled = true; + set |= Signal::SOCKET_PEER_WRITE_DISABLED; + } + if options.contains(SocketOptions::SHUTDOWN_WRITE) { + clear |= Signal::WRITABLE; + set |= Signal::SOCKET_WRITE_DISABLED; + } + self.signal_change(clear, set); + Ok(()) + } + + pub fn set_read_threshold(&self, threshold: usize) -> ZxResult { + let _ = self.lock.lock(); + let mut inner = self.inner.lock(); + inner.read_threshold = threshold; + if threshold == 0 { + self.signal_change(Signal::SOCKET_READ_THRESHOLD, Signal::empty()); + } else if inner.data.len() >= threshold { + self.signal_change(Signal::empty(), Signal::SOCKET_READ_THRESHOLD); + } else { + self.signal_change(Signal::SOCKET_READ_THRESHOLD, Signal::empty()); + } + Ok(()) + } + + pub fn set_write_threshold(&self, threshold: usize) -> ZxResult { + let _ = self.lock.lock(); + let peer = self.peer.upgrade(); + if peer.is_none() { + return Err(ZxError::PEER_CLOSED); + } + peer.unwrap().inner.lock().write_threshold = threshold; + Ok(()) + } + + pub fn get_rx_tx_threshold(&self) -> (usize, usize) { + let inner = self.inner.lock(); + (inner.read_threshold, inner.write_threshold) + } +} + +impl Drop for Socket { + fn drop(&mut self) { + if let Some(peer) = self.peer.upgrade() { + peer.signal_change(Signal::WRITABLE, Signal::PEER_CLOSED); + } + } +} + +#[repr(C)] +#[derive(Default)] +pub struct SocketInfo { + options: u32, + padding1: u32, + rx_buf_max: u64, + rx_buf_size: u64, + rx_buf_available: u64, + tx_buf_max: u64, + tx_buf_size: u64, +} + +bitflags! { + #[derive(Default)] + pub struct SocketOptions: u32 { + #[allow(clippy::identity_op)] + const SHUTDOWN_WRITE = 1 << 0; + const SHUTDOWN_READ = 1 << 1; + #[allow(clippy::identity_op)] + const DATAGRAM = 1 << 0; + const PEEK = 1 << 3; + } } diff --git a/zircon-object/src/object/rights.rs b/zircon-object/src/object/rights.rs index 0d1b42c00..07f18dfb7 100644 --- a/zircon-object/src/object/rights.rs +++ b/zircon-object/src/object/rights.rs @@ -95,6 +95,7 @@ bitflags! { const DEFAULT_FIFO = Self::BASIC.bits | Self::IO.bits | Self::SIGNAL.bits | Self::SIGNAL_PEER.bits; const DEFAULT_SOCKET = Self::BASIC.bits | Self::IO.bits | Self::PROPERTY.bits | Self::SIGNAL.bits | Self::SIGNAL_PEER.bits; const DEFAULT_BTI = (Self::BASIC.bits & !Self::WAIT.bits) | Self::IO.bits | Self::MAP.bits; + const DEFAULT_INTERRUPT = Self::BASIC.bits | Self::IO.bits | Self::SIGNAL.bits; } } diff --git a/zircon-object/src/object/signal.rs b/zircon-object/src/object/signal.rs index 36d7ed8f3..bac1077fe 100644 --- a/zircon-object/src/object/signal.rs +++ b/zircon-object/src/object/signal.rs @@ -35,6 +35,8 @@ bitflags! { const VMO_ZERO_CHILDREN = Self::SIGNALED.bits; + const INTERRUPT_SIGNAL = 1 << 4; + // for Linux const SIGCHLD = 1 << 6; diff --git a/zircon-object/src/signal/port.rs b/zircon-object/src/signal/port.rs index 4c7adf835..de39d6e68 100644 --- a/zircon-object/src/signal/port.rs +++ b/zircon-object/src/signal/port.rs @@ -1,8 +1,10 @@ pub use self::port_packet::*; use super::*; use crate::object::*; +use alloc::collections::btree_map::BTreeMap; use alloc::collections::vec_deque::VecDeque; use alloc::sync::Arc; +use bitflags::bitflags; use spin::Mutex; #[path = "port_packet.rs"] @@ -18,6 +20,7 @@ mod port_packet; /// asynchronous message delivery from IPC transports. pub struct Port { base: KObjectBase, + options: PortOptions, inner: Mutex, } @@ -26,13 +29,35 @@ impl_kobject!(Port); #[derive(Default, Debug)] struct PortInner { queue: VecDeque, + interrupt_queue: VecDeque, + interrupt_grave: BTreeMap, + interrupt_pid: u64, +} + +#[derive(Default, Debug)] +pub struct PortInterruptPacket { + timestamp: i64, + key: u64, + pid: u64, +} + +impl From for PacketInterrupt { + fn from(packet: PortInterruptPacket) -> Self { + PacketInterrupt { + timestamp: packet.timestamp, + reserved0: 0, + reserved1: 0, + reserved2: 0, + } + } } impl Port { /// Create a new `Port`. - pub fn new() -> Arc { + pub fn new(options: u32) -> Arc { Arc::new(Port { base: KObjectBase::default(), + options: PortOptions::from_bits_truncate(options), inner: Mutex::default(), }) } @@ -45,14 +70,65 @@ impl Port { self.base.signal_set(Signal::READABLE); } + // Push an `InterruptPacket` into the port. + pub fn push_interrupt(&self, timestamp: i64, key: u64) -> u64 { + let mut inner = self.inner.lock(); + inner.interrupt_pid += 1; + let pid = inner.interrupt_pid; + inner.interrupt_queue.push_back(PortInterruptPacket { + timestamp, + key, + pid, + }); + inner.interrupt_grave.insert(pid, true); + drop(inner); + self.base.signal_set(Signal::READABLE); + pid + } + + // Remove an `InterruptPacket` from the port. + // Return whether the packet is in the port + pub fn remove_interrupt(&self, pid: u64) -> bool { + let mut inner = self.inner.lock(); + match inner.interrupt_grave.get(&pid) { + Some(in_queue) => { + let in_queue = *in_queue; + inner.interrupt_grave.insert(pid, false); + in_queue + } + None => false, + } + } + /// Asynchronous wait until at least one packet is available, then take out all packets. pub async fn wait(self: &Arc) -> PortPacket { let object = self.clone() as Arc; loop { object.wait_signal(Signal::READABLE).await; let mut inner = self.inner.lock(); + if self.can_bind_to_interrupt() { + while let Some(packet) = inner.interrupt_queue.pop_front() { + let in_queue = inner.interrupt_grave.remove(&packet.pid).unwrap(); + if inner.queue.is_empty() + && (inner.interrupt_queue.is_empty() || !self.can_bind_to_interrupt()) + { + self.base.signal_clear(Signal::READABLE); + } + if !in_queue { + continue; + } + return PortPacketRepr { + key: packet.key, + status: ZxError::OK, + data: PayloadRepr::Interrupt(packet.into()), + } + .into(); + } + } if let Some(packet) = inner.queue.pop_front() { - if inner.queue.is_empty() { + if inner.queue.is_empty() + && (inner.interrupt_queue.is_empty() || !self.can_bind_to_interrupt()) + { self.base.signal_clear(Signal::READABLE); } return packet; @@ -65,6 +141,17 @@ impl Port { fn len(&self) -> usize { self.inner.lock().queue.len() } + + pub fn can_bind_to_interrupt(&self) -> bool { + self.options.contains(PortOptions::BIND_TO_INTERUPT) + } +} + +bitflags! { + pub struct PortOptions: u32 { + #[allow(clippy::identity_op)] + const BIND_TO_INTERUPT = 1 << 0; + } } #[cfg(test)] @@ -74,7 +161,7 @@ mod tests { #[async_std::test] async fn wait() { - let port = Port::new(); + let port = Port::new(0); let object = DummyObject::new() as Arc; object.send_signal_to_port_async(Signal::READABLE, &port, 1); diff --git a/zircon-object/src/signal/port_packet.rs b/zircon-object/src/signal/port_packet.rs index 76ec182d0..064c62ea1 100644 --- a/zircon-object/src/signal/port_packet.rs +++ b/zircon-object/src/signal/port_packet.rs @@ -20,7 +20,7 @@ pub struct PortPacket { #[repr(u8)] #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[allow(dead_code)] -enum PacketType { +pub enum PacketType { User = 0, SignalOne = 1, SignalRep = 2, @@ -37,6 +37,7 @@ enum PacketType { union Payload { signal: PacketSignal, exception: PacketException, + interrupt: PacketInterrupt, user: [u8; 32], } @@ -57,6 +58,15 @@ pub struct PacketException { pub num: u8, } +#[repr(C)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct PacketInterrupt { + pub timestamp: i64, + pub reserved0: u64, + pub reserved1: u64, + pub reserved2: u64, +} + // Rust struct: for internal constructing and debugging /// A high-level representation of a packet sent through a port. @@ -72,6 +82,7 @@ pub struct PortPacketRepr { pub enum PayloadRepr { Signal(PacketSignal), Exception(PacketException), + Interrupt(PacketInterrupt), User([u8; 32]), } @@ -87,12 +98,14 @@ impl PayloadRepr { PayloadRepr::User(_) => PacketType::User, PayloadRepr::Signal(_) => PacketType::SignalOne, PayloadRepr::Exception(_) => PacketType::Exception, + PayloadRepr::Interrupt(_) => PacketType::Interrupt, } } fn encode(&self) -> Payload { match *self { PayloadRepr::Signal(signal) => Payload { signal }, PayloadRepr::Exception(exception) => Payload { exception }, + PayloadRepr::Interrupt(interrupt) => Payload { interrupt }, PayloadRepr::User(user) => Payload { user }, } } @@ -107,6 +120,7 @@ impl PayloadRepr { num: exception_num, ..data.exception }), + PacketType::Interrupt => PayloadRepr::Interrupt(data.interrupt), _ => unimplemented!(), } } diff --git a/zircon-object/src/task/thread.rs b/zircon-object/src/task/thread.rs index 539c69f7c..6e88c0a0d 100644 --- a/zircon-object/src/task/thread.rs +++ b/zircon-object/src/task/thread.rs @@ -120,6 +120,16 @@ struct ThreadInner { time: u128, } +impl ThreadInner { + pub fn get_state(&self) -> ThreadState { + if self.suspend_count == 0 { + self.state + } else { + ThreadState::Suspended + } + } +} + impl Thread { /// Create a new thread. pub fn create(proc: &Arc, name: &str, _options: u32) -> ZxResult> { @@ -283,7 +293,7 @@ impl Thread { pub fn get_thread_info(&self) -> ThreadInfo { let inner = self.inner.lock(); ThreadInfo { - state: inner.state as u32, + state: inner.get_state() as u32, wait_exception_type: 0, cpu_affnity_mask: [0u64; 8], } @@ -336,12 +346,7 @@ impl Thread { } pub fn state(&self) -> ThreadState { - let inner = self.inner.lock(); - if inner.suspend_count == 0 { - inner.state - } else { - ThreadState::Suspended - } + self.inner.lock().get_state() } pub fn get_exceptionate(&self) -> Arc { diff --git a/zircon-syscall/src/ddk.rs b/zircon-syscall/src/ddk.rs index 5a7741e8b..c22f37579 100644 --- a/zircon-syscall/src/ddk.rs +++ b/zircon-syscall/src/ddk.rs @@ -2,8 +2,7 @@ use { super::*, bitflags::bitflags, kernel_hal::DevVAddr, - zircon_object::dev::*, - zircon_object::vm::{page_aligned, VmObject}, + zircon_object::{dev::*, signal::*, task::*, vm::*}, }; impl Syscall<'_> { @@ -139,6 +138,115 @@ impl Syscall<'_> { smbios_ptr.write(smbios)?; Ok(()) } + + pub fn sys_interrupt_create( + &self, + resource: HandleValue, + src_num: usize, + options: u32, + mut out: UserOutPtr, + ) -> ZxResult { + info!( + "interrupt.create: handle={:?} src_num={:?} options={:?}", + resource, src_num, options + ); + let proc = self.thread.proc(); + let options = InterruptOptions::from_bits_truncate(options); + if options.contains(InterruptOptions::VIRTUAL) { + let interrupt = Interrupt::new_virtual(options)?; + let handle = proc.add_handle(Handle::new(interrupt, Rights::DEFAULT_INTERRUPT)); + out.write(handle)?; + } else { + let resource = proc.get_object::(resource)?; + resource.validate_ranged_resource(ResourceKind::IRQ, src_num, 1)?; + let interrupt = Interrupt::new_event(src_num, options)?; + let handle = proc.add_handle(Handle::new(interrupt, Rights::DEFAULT_INTERRUPT)); + out.write(handle)?; + } + // redundant add_handle & out.write, how to merge it? + Ok(()) + } + + pub fn sys_interrupt_bind( + &self, + interrupt: HandleValue, + port: HandleValue, + key: u64, + options: u32, + ) -> ZxResult { + info!( + "interrupt.bind: interrupt={:?} port={:?} key={:?} options={:?}", + interrupt, port, key, options + ); + let proc = self.thread.proc(); + let interrupt = proc.get_object_with_rights::(interrupt, Rights::READ)?; + let port = proc.get_object_with_rights::(port, Rights::WRITE)?; + if !port.can_bind_to_interrupt() { + return Err(ZxError::WRONG_TYPE); + } + if options == InterruptOp::Bind as _ { + interrupt.bind(port, key) + } else if options == InterruptOp::Unbind as _ { + interrupt.unbind(port) + } else { + Err(ZxError::INVALID_ARGS) + } + } + + pub fn sys_interrupt_trigger( + &self, + interrupt: HandleValue, + options: u32, + timestamp: i64, + ) -> ZxResult { + info!( + "interrupt.trigger: interrupt={:?} options={:?} timestamp={:?}", + interrupt, options, timestamp + ); + let interrupt = self + .thread + .proc() + .get_object_with_rights::(interrupt, Rights::SIGNAL)?; + interrupt.trigger(timestamp) + } + + pub fn sys_interrupt_ack(&self, interrupt: HandleValue) -> ZxResult { + info!("interupt.ack: interrupt={:?}", interrupt); + let interrupt = self + .thread + .proc() + .get_object_with_rights::(interrupt, Rights::WRITE)?; + interrupt.ack() + } + + pub fn sys_interrupt_destroy(&self, interrupt: HandleValue) -> ZxResult { + info!("interupt.destory: interrupt={:?}", interrupt); + let interrupt = self.thread.proc().get_object::(interrupt)?; + interrupt.destroy() + } + + pub async fn sys_interrupt_wait( + &self, + interrupt: HandleValue, + mut out: UserOutPtr, + ) -> ZxResult { + info!("interrupt.wait: handle={:?}", interrupt); + assert_eq!(core::mem::size_of::(), 48); + let proc = self.thread.proc(); + let interrupt = proc.get_object_with_rights::(interrupt, Rights::WAIT)?; + let future = interrupt.wait(); + pin_mut!(future); + let timestamp = self + .thread + .blocking_run( + future, + ThreadState::BlockedInterrupt, + Deadline::forever().into(), + ) + .await?; + out.write_if_not_null(timestamp)?; + Ok(()) + } } const IOMMU_TYPE_DUMMY: u32 = 0; @@ -156,6 +264,11 @@ bitflags! { } } +enum InterruptOp { + Bind = 0, + Unbind = 1, +} + impl BtiOptions { /// Get desired rights of VMO handle. fn to_vmo_rights(self) -> Rights { diff --git a/zircon-syscall/src/lib.rs b/zircon-syscall/src/lib.rs index 0d5ad0590..769dc85b8 100644 --- a/zircon-syscall/src/lib.rs +++ b/zircon-syscall/src/lib.rs @@ -158,6 +158,13 @@ impl Syscall<'_> { self.sys_channel_call_finish(a0.into(), a1.into(), a2.into(), a3.into()) } Sys::SOCKET_CREATE => self.sys_socket_create(a0 as _, a1.into(), a2.into()), + Sys::SOCKET_WRITE => { + self.sys_socket_write(a0 as _, a1 as _, a2.into(), a3 as _, a4.into()) + } + Sys::SOCKET_READ => { + self.sys_socket_read(a0 as _, a1 as _, a2.into(), a3 as _, a4.into()) + } + Sys::SOCKET_SHUTDOWN => self.sys_socket_shutdown(a0 as _, a1 as _), Sys::FIFO_CREATE => { self.sys_fifo_create(a0 as _, a1 as _, a2 as _, a3.into(), a4.into()) } @@ -168,6 +175,10 @@ impl Syscall<'_> { Sys::PORT_CREATE => self.sys_port_create(a0 as _, a1.into()), Sys::PORT_WAIT => self.sys_port_wait(a0 as _, a1.into(), a2.into()).await, Sys::PORT_QUEUE => self.sys_port_queue(a0 as _, a1.into()), + Sys::PORT_CANCEL => { + error!("Skip PORT_CANCEL"); + Ok(()) + } Sys::FUTEX_WAIT => { self.sys_futex_wait(a0.into(), a1 as _, a2 as _, a3.into()) .await @@ -276,6 +287,18 @@ impl Syscall<'_> { self.sys_object_get_child(a0 as _, a1 as _, a2 as _, a3.into()) } Sys::PC_FIRMWARE_TABLES => self.sys_pc_firmware_tables(a0 as _, a1.into(), a2.into()), + Sys::INTERRUPT_CREATE => { + self.sys_interrupt_create(a0 as _, a1 as _, a2 as _, a3.into()) + } + Sys::INTERRUPT_BIND => self.sys_interrupt_bind(a0 as _, a1 as _, a2 as _, a3 as _), + Sys::INTERRUPT_TRIGGER => self.sys_interrupt_trigger(a0 as _, a1 as _, a2 as _), + Sys::INTERRUPT_ACK => self.sys_interrupt_ack(a0 as _), + Sys::INTERRUPT_DESTROY => self.sys_interrupt_destroy(a0 as _), + Sys::INTERRUPT_WAIT => self.sys_interrupt_wait(a0 as _, a1.into()).await, + Sys::IOPORTS_REQUEST => { + error!("Skip IOPORTS_REQUEST"); + Ok(()) + } _ => { error!("syscall unimplemented: {:?}", sys_type); Err(ZxError::NOT_SUPPORTED) diff --git a/zircon-syscall/src/object.rs b/zircon-syscall/src/object.rs index 41072b233..3e5d1f597 100644 --- a/zircon-syscall/src/object.rs +++ b/zircon-syscall/src/object.rs @@ -3,7 +3,7 @@ use { alloc::vec::Vec, core::convert::TryFrom, numeric_enum_macro::numeric_enum, - zircon_object::{dev::*, signal::Port, task::*, vm::*}, + zircon_object::{dev::*, ipc::*, signal::Port, task::*, vm::*}, }; impl Syscall<'_> { @@ -62,8 +62,30 @@ impl Syscall<'_> { UserOutPtr::::from(ptr).write(break_on_load)?; Ok(()) } + Property::SocketRxThreshold => { + if buffer_size < 8 { + return Err(ZxError::BUFFER_TOO_SMALL); + } + let rx = proc + .get_object_with_rights::(handle_value, Rights::GET_PROPERTY)? + .get_rx_tx_threshold() + .0; + UserOutPtr::::from(ptr).write(rx)?; + Ok(()) + } + Property::SocketTxThreshold => { + if buffer_size < 8 { + return Err(ZxError::BUFFER_TOO_SMALL); + } + let tx = proc + .get_object_with_rights::(handle_value, Rights::GET_PROPERTY)? + .get_rx_tx_threshold() + .1; + UserOutPtr::::from(ptr).write(tx)?; + Ok(()) + } _ => { - warn!("unknown property"); + warn!("unknown property {:?}", property); Err(ZxError::INVALID_ARGS) } } @@ -120,6 +142,22 @@ impl Syscall<'_> { .set_dyn_break_on_load(addr); Ok(()) } + Property::SocketRxThreshold => { + if buffer_size < 8 { + return Err(ZxError::BUFFER_TOO_SMALL); + } + let threshold = UserInPtr::::from(ptr).read()?; + proc.get_object::(handle_value)? + .set_read_threshold(threshold) + } + Property::SocketTxThreshold => { + if buffer_size < 8 { + return Err(ZxError::BUFFER_TOO_SMALL); + } + let threshold = UserInPtr::::from(ptr).read()?; + proc.get_object::(handle_value)? + .set_write_threshold(threshold) + } _ => { warn!("unknown property"); Err(ZxError::INVALID_ARGS) @@ -208,7 +246,7 @@ impl Syscall<'_> { UserOutPtr::::from(buffer).write(job.get_info())?; } Topic::ProcessVmos => { - warn!("A dummy implementation for utest Bti.NoDelayedUnpin, it does not check the reture value"); + error!("A dummy implementation for utest Bti.NoDelayedUnpin, it does not check the reture value"); actual.write(0)?; avail.write(0)?; } @@ -256,6 +294,14 @@ impl Syscall<'_> { .get_object_with_rights::(handle, Rights::INSPECT)?; UserOutPtr::::from(buffer).write(bti.get_info())?; } + Topic::Resource => { + let resource = proc.get_object_with_rights::(handle, Rights::INSPECT)?; + UserOutPtr::::from(buffer).write(resource.get_info())?; + } + Topic::Socket => { + let socket = proc.get_object_with_rights::(handle, Rights::INSPECT)?; + UserOutPtr::::from(buffer).write(socket.get_info())?; + } _ => { error!("not supported info topic: {:?}", topic); return Err(ZxError::NOT_SUPPORTED); @@ -426,6 +472,8 @@ numeric_enum! { ProcessDebugAddr = 5, ProcessVdsoBaseAddress = 6, ProcessBreakOnLoad = 7, + SocketRxThreshold = 12, + SocketTxThreshold = 13, } } diff --git a/zircon-syscall/src/port.rs b/zircon-syscall/src/port.rs index 2105c72aa..e3e5db16c 100644 --- a/zircon-syscall/src/port.rs +++ b/zircon-syscall/src/port.rs @@ -6,10 +6,7 @@ use { impl Syscall<'_> { pub fn sys_port_create(&self, options: u32, mut out: UserOutPtr) -> ZxResult { info!("port.create: options={:#x}", options); - if options != 0 { - unimplemented!() - } - let port_handle = Handle::new(Port::new(), Rights::DEFAULT_PORT); + let port_handle = Handle::new(Port::new(options), Rights::DEFAULT_PORT); let handle_value = self.thread.proc().add_handle(port_handle); out.write(handle_value)?; Ok(()) diff --git a/zircon-syscall/src/socket.rs b/zircon-syscall/src/socket.rs index 9d9021370..a1825152c 100644 --- a/zircon-syscall/src/socket.rs +++ b/zircon-syscall/src/socket.rs @@ -1,12 +1,17 @@ -use {super::*, zircon_object::ipc::Socket}; +use {super::*, zircon_object::ipc::*}; impl Syscall<'_> { pub fn sys_socket_create( &self, - _options: u32, + options: u32, mut out0: UserOutPtr, mut out1: UserOutPtr, ) -> ZxResult { + info!("socket.create: options={:#x?}", options); + if options != 0 { + error!("socket.create: only implemented options=0"); + return Err(ZxError::NOT_SUPPORTED); + } let (end0, end1) = Socket::create(); let proc = self.thread.proc(); let handle0 = proc.add_handle(Handle::new(end0, Rights::DEFAULT_SOCKET)); @@ -15,4 +20,64 @@ impl Syscall<'_> { out1.write(handle1)?; Ok(()) } + + pub fn sys_socket_write( + &self, + socket: HandleValue, + options: u32, + buffer: UserInPtr, + size: usize, + mut actual_size: UserOutPtr, + ) -> ZxResult { + info!( + "socket.write: socket={:#x?} options={:#x?} buffer={:#x?} size={:#x?}", + socket, options, buffer, size, + ); + if options != 0 { + unimplemented!(); + } + let socket = self + .thread + .proc() + .get_object_with_rights::(socket, Rights::WRITE)?; + let buffer = buffer.read_array(size)?; + let size = socket.write(buffer)?; + actual_size.write_if_not_null(size)?; + Ok(()) + } + + pub fn sys_socket_read( + &self, + socket: HandleValue, + options: u32, + mut buffer: UserOutPtr, + size: usize, + mut actual_size: UserOutPtr, + ) -> ZxResult { + info!( + "socket.read: socket={:#x?} options={:#x?} buffer={:#x?} size={:#x?}", + socket, options, buffer, size, + ); + let socket = self + .thread + .proc() + .get_object_with_rights::(socket, Rights::READ)?; + let result = socket.read(size, SocketOptions::from_bits_truncate(options))?; + actual_size.write_if_not_null(result.len())?; + buffer.write_array(&result)?; + Ok(()) + } + + pub fn sys_socket_shutdown(&self, socket: HandleValue, options: u32) -> ZxResult { + info!( + "socket.shutdown: socket={:#x?} options={:#x?}", + socket, options + ); + let socket = self + .thread + .proc() + .get_object_with_rights::(socket, Rights::WRITE)?; + socket.shutdown(SocketOptions::from_bits_truncate(options))?; + Ok(()) + } } diff --git a/zircon-syscall/src/time.rs b/zircon-syscall/src/time.rs index 619d13081..fdef4b18c 100644 --- a/zircon-syscall/src/time.rs +++ b/zircon-syscall/src/time.rs @@ -91,6 +91,10 @@ impl Deadline { pub fn is_positive(&self) -> bool { self.0.is_positive() } + + pub fn forever() -> Self { + Deadline(i64::max_value()) + } } impl From for Duration {