//! Thin wrappers over the kernel's register ABI (docs/ipc.md). Every call //! returns a status in `a0`; results follow in `a1..`. use crate::arch; use types::address::{EntryPoint, StackPointer, VirtualAddress}; use types::badge::Badge; use types::bounded::{CapSlot, PageCount}; use types::id::ProcessId; use types::mapping::PagePerms; use types::permission::PermissionSet; use types::syscall_abi::*; use types::time::Ticks; pub use types::syscall_abi::ErrorCode as Error; pub type Result = core::result::Result; fn raw(nr: usize, args: [usize; 6]) -> Result<[usize; 8]> { let r = arch::syscall(nr, args); match r[0] { 0 => Ok(r), code => Err(Error::from_raw(code).unwrap_or(Error::InvalidArgument)), } } fn call0(nr: usize) -> Result<[usize; 8]> { raw(nr, [0; 6]) } fn slot_result(r: usize) -> Option { reg_slot(r).flatten() } /// What a receiving call got. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Received { /// A `Send`. `badge` identifies the sender's capability. Message { badge: usize, words: [usize; 4], cap: Option, }, /// A `Call`; answer it with `reply` or `reply_recv`. Call { badge: usize, words: [usize; 4], cap: Option, }, /// Bits from a notification. Signal { bits: usize }, } impl Received { fn decode(r: [usize; 8]) -> Result { let d = regs_delivery(&r).ok_or(Error::InvalidArgument)?; let (badge, words, cap) = (d.badge, d.words, d.cap); Ok(match d.kind { DeliveryKind::Message => Received::Message { badge, words, cap }, DeliveryKind::Call => Received::Call { badge, words, cap }, DeliveryKind::Signal => Received::Signal { bits: badge }, }) } pub fn words(&self) -> [usize; 4] { match self { Received::Message { words, .. } | Received::Call { words, .. } => *words, Received::Signal { .. } => [0; 4], } } } fn slot_of(r: Result<[usize; 8]>) -> Result { r.and_then(|r| slot_result(r[1]).ok_or(Error::InvalidArgument)) } // ---- threads, processes, time ---- pub fn exit() -> ! { let _ = call0(SYS_EXIT); loop {} } pub fn exit_thread() -> ! { let _ = call0(SYS_EXIT_THREAD); loop {} } pub fn yield_now() { let _ = call0(SYS_YIELD); } pub fn halt() -> ! { let _ = call0(SYS_HALT); loop {} } pub fn get_time() -> Ticks { Ticks::from_raw(call0(SYS_GET_TIME).map_or(0, |r| r[1]) as u64) } pub fn get_process_id() -> ProcessId { ProcessId::from_raw(call0(SYS_GET_PROCESS_ID).map_or(0, |r| r[1]) as u16) } pub fn create_process(create_cap: CapSlot) -> Option { slot_of(raw( SYS_CREATE_PROCESS, [create_cap.value() as usize, 0, 0, 0, 0, 0], )) .ok() } pub fn spawn_thread(entry: EntryPoint, stack: StackPointer) -> usize { raw(SYS_SPAWN_THREAD, [entry.raw(), stack.raw(), 0, 0, 0, 0]).map_or(NONE, |r| r[1]) } pub fn spawn_thread_in(process_cap: CapSlot, entry: EntryPoint, stack: StackPointer) -> usize { raw( SYS_SPAWN_THREAD_IN, [ process_cap.value() as usize, entry.raw(), stack.raw(), 0, 0, 0, ], ) .map_or(NONE, |r| r[1]) } pub fn kill(process_cap: CapSlot) -> bool { raw(SYS_KILL, [process_cap.value() as usize, 0, 0, 0, 0, 0]).is_ok() } // ---- memory ---- pub fn request_pages(count: PageCount) -> Option { raw(SYS_REQUEST_PAGES, [count.value() as usize, 0, 0, 0, 0, 0]) .ok() .map(|r| VirtualAddress::from_raw(r[1])) } pub fn map_page( process_cap: CapSlot, src_va: VirtualAddress, dst_va: VirtualAddress, executable: bool, ) -> bool { raw( SYS_MAP_PAGE, [ process_cap.value() as usize, src_va.raw(), dst_va.raw(), executable as usize, 0, 0, ], ) .is_ok() } /// Allocates `pages` of physically contiguous shared memory, charged to /// this process, and returns a capability to it. pub fn create_region(pages: PageCount) -> Result { slot_of(raw( SYS_CREATE_REGION, [pages.value() as usize, 0, 0, 0, 0, 0], )) } /// Maps the region behind `region` at `va`. The mapping lasts as long as /// the capability: dropping or having it revoked unmaps it. pub fn map_region(region: CapSlot, va: VirtualAddress, perms: PagePerms) -> Result<()> { let perms = match perms { PagePerms::ReadOnly => PERMS_READ_ONLY, PagePerms::ReadWrite => PERMS_READ_WRITE, PagePerms::ReadExecute => PERMS_READ_EXECUTE, }; raw( SYS_MAP_REGION, [region.value() as usize, va.raw(), perms, 0, 0, 0], ) .map(|_| ()) } // ---- capabilities ---- pub fn delegate(slot: CapSlot, to: CapSlot, permissions: PermissionSet) -> bool { raw( SYS_DELEGATE, [ slot.value() as usize, to.value() as usize, permissions.bits() as usize, 0, 0, 0, ], ) .is_ok() } /// Copies an unbadged endpoint or notification capability with `badge` /// and at most `permissions`. pub fn mint(slot: CapSlot, badge: Badge, permissions: PermissionSet) -> Result { slot_of(raw( SYS_MINT, [ slot.value() as usize, badge.raw(), permissions.bits() as usize, 0, 0, 0, ], )) } /// Deletes every capability derived from `slot` (in any process) and keeps /// `slot` itself. pub fn revoke(slot: CapSlot) -> bool { raw(SYS_REVOKE, [slot.value() as usize, 0, 0, 0, 0, 0]).is_ok() } /// Removes a capability from this process's own table, freeing the slot. pub fn drop_cap(slot: CapSlot) -> bool { raw(SYS_DROP_CAP, [slot.value() as usize, 0, 0, 0, 0, 0]).is_ok() } // ---- endpoints ---- pub fn create_endpoint() -> Option { slot_of(call0(SYS_CREATE_ENDPOINT)).ok() } /// Sends four words; returns 0 on success (kept for existing callers). pub fn send(endpoint: CapSlot, w0: usize, w1: usize, w2: usize, w3: usize) -> usize { match send_msg(endpoint, [w0, w1, w2, w3], None) { Ok(()) => 0, Err(e) => e as usize, } } pub fn send_msg(endpoint: CapSlot, words: [usize; 4], cap: Option) -> Result<()> { raw( SYS_SEND, message_regs(endpoint.value() as usize, words, cap), ) .map(|_| ()) } /// Receives and returns just the words (kept for existing callers). pub fn receive(endpoint: CapSlot) -> (usize, usize, usize, usize) { let w = receive_msg(endpoint).map_or([0; 4], |r| r.words()); (w[0], w[1], w[2], w[3]) } pub fn receive_msg(endpoint: CapSlot) -> Result { raw(SYS_RECEIVE, [endpoint.value() as usize, 0, 0, 0, 0, 0]).and_then(Received::decode) } /// Sends and waits for the reply. pub fn call(endpoint: CapSlot, words: [usize; 4], cap: Option) -> Result { raw( SYS_CALL, message_regs(endpoint.value() as usize, words, cap), ) .and_then(Received::decode) } /// Answers the last `Call` this thread received. pub fn reply(words: [usize; 4], cap: Option) -> Result<()> { raw(SYS_REPLY, message_regs(0, words, cap)).map(|_| ()) } /// Replies to the pending `Call` (if any), then receives the next message. pub fn reply_recv(endpoint: CapSlot, words: [usize; 4], cap: Option) -> Result { raw( SYS_REPLY_RECV, message_regs(endpoint.value() as usize, words, cap), ) .and_then(Received::decode) } // ---- notifications and interrupts ---- pub fn create_notification() -> Result { slot_of(call0(SYS_CREATE_NOTIFICATION)) } pub fn signal(notification: CapSlot) -> Result<()> { raw(SYS_SIGNAL, [notification.value() as usize, 0, 0, 0, 0, 0]).map(|_| ()) } /// Blocks until the notification has bits, then returns and clears them. pub fn wait(notification: CapSlot) -> Result { raw(SYS_WAIT, [notification.value() as usize, 0, 0, 0, 0, 0]).map(|r| r[1]) } /// Returns and clears the bits without blocking. pub fn poll(notification: CapSlot) -> Result { raw(SYS_POLL, [notification.value() as usize, 0, 0, 0, 0, 0]).map(|r| r[1]) } /// After this, `receive` also returns when the notification is signalled. pub fn bind_notification(notification: CapSlot) -> Result<()> { raw( SYS_BIND_NOTIFICATION, [notification.value() as usize, 0, 0, 0, 0, 0], ) .map(|_| ()) } /// Delivers interrupt `irq` as a signal on `notification`. pub fn set_irq_notification(irq: CapSlot, notification: CapSlot) -> Result<()> { raw( SYS_SET_IRQ_NOTIFICATION, [ irq.value() as usize, notification.value() as usize, 0, 0, 0, 0, ], ) .map(|_| ()) } pub fn ack_interrupt(irq: CapSlot) -> Result<()> { raw( SYS_ACKNOWLEDGE_INTERRUPT, [irq.value() as usize, 0, 0, 0, 0, 0], ) .map(|_| ()) } pub fn debug_putchar(byte: u8) { let _ = raw(SYS_DEBUG_PUTCHAR, [byte as usize, 0, 0, 0, 0, 0]); }