Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
hubris/sys/userlib/src/lib.rs /
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
954 lines (835 sloc)
27.1 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // This Source Code Form is subject to the terms of the Mozilla Public | |
| // License, v. 2.0. If a copy of the MPL was not distributed with this | |
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | |
| //! User application support library for Hubris. | |
| //! | |
| //! This contains syscall stubs and types, and re-exports the contents of the | |
| //! `abi` crate that gets shared with the kernel. | |
| //! | |
| //! # Syscall stub implementations | |
| //! | |
| //! Each syscall stub consists of two parts: a public `sys_foo` function | |
| //! intended for use by programs, and an internal `sys_foo_stub` function. This | |
| //! might seem like needless duplication, and in a way, it is. | |
| //! | |
| //! Limitations in the behavior of the current `asm!` feature mean we have a | |
| //! hard time moving values into registers r6, r7, and r11. Because (for better | |
| //! or worse) the syscall ABI uses these registers, we have to take extra steps. | |
| //! | |
| //! The `stub` function contains the actual `asm!` call sequence. It is `naked`, | |
| //! meaning the compiler will *not* attempt to do any framepointer/basepointer | |
| //! nonsense, and we can thus reason about the assignment and availability of | |
| //! all registers. | |
| //! | |
| //! See: https://github.com/rust-lang/rust/issues/73450#issuecomment-650463347 | |
| #![no_std] | |
| #![feature(asm)] | |
| #![feature(naked_functions)] | |
| #[macro_use] | |
| pub mod macros; | |
| pub use abi::*; | |
| pub use num_derive::{FromPrimitive, ToPrimitive}; | |
| pub use num_traits::{FromPrimitive, ToPrimitive}; | |
| use core::marker::PhantomData; | |
| pub mod hl; | |
| pub mod kipc; | |
| pub mod task_slot; | |
| pub mod units; | |
| pub mod util; | |
| #[derive(Debug)] | |
| #[repr(transparent)] | |
| pub struct Lease<'a> { | |
| _kern_rep: abi::ULease, | |
| _marker: PhantomData<&'a mut ()>, | |
| } | |
| impl<'a> From<&'a [u8]> for Lease<'a> { | |
| fn from(x: &'a [u8]) -> Self { | |
| Self { | |
| _kern_rep: abi::ULease { | |
| attributes: abi::LeaseAttributes::READ, | |
| base_address: x.as_ptr() as u32, | |
| length: x.len() as u32, | |
| }, | |
| _marker: PhantomData, | |
| } | |
| } | |
| } | |
| impl<'a> From<&'a mut [u8]> for Lease<'a> { | |
| fn from(x: &'a mut [u8]) -> Self { | |
| Self { | |
| _kern_rep: abi::ULease { | |
| attributes: LeaseAttributes::READ | LeaseAttributes::WRITE, | |
| base_address: x.as_ptr() as u32, | |
| length: x.len() as u32, | |
| }, | |
| _marker: PhantomData, | |
| } | |
| } | |
| } | |
| /// Return type for stubs that return an `(rc, len)` tuple, because the layout | |
| /// of tuples is not specified in the C ABI, and we're using the C ABI to | |
| /// interface to assembler. | |
| /// | |
| /// Register-return of structs is also not guaranteed by the C ABI, so we | |
| /// represent the pair of returned registers with something that *can* get | |
| /// passed back in registers: a `u64`. | |
| #[repr(transparent)] | |
| struct RcLen(u64); | |
| impl From<RcLen> for (u32, usize) { | |
| fn from(s: RcLen) -> Self { | |
| (s.0 as u32, (s.0 >> 32) as usize) | |
| } | |
| } | |
| #[inline(always)] | |
| pub fn sys_send( | |
| target: TaskId, | |
| operation: u16, | |
| outgoing: &[u8], | |
| incoming: &mut [u8], | |
| leases: &[Lease<'_>], | |
| ) -> (u32, usize) { | |
| let mut args = SendArgs { | |
| packed_target_operation: u32::from(target.0) << 16 | |
| | u32::from(operation), | |
| outgoing_ptr: outgoing.as_ptr(), | |
| outgoing_len: outgoing.len(), | |
| incoming_ptr: incoming.as_mut_ptr(), | |
| incoming_len: incoming.len(), | |
| lease_ptr: leases.as_ptr(), | |
| lease_len: leases.len(), | |
| }; | |
| unsafe { sys_send_stub(&mut args).into() } | |
| } | |
| #[allow(dead_code)] // this gets used from asm | |
| #[repr(C)] // field order matters | |
| struct SendArgs<'a> { | |
| packed_target_operation: u32, | |
| outgoing_ptr: *const u8, | |
| outgoing_len: usize, | |
| incoming_ptr: *mut u8, | |
| incoming_len: usize, | |
| lease_ptr: *const Lease<'a>, | |
| lease_len: usize, | |
| } | |
| /// Core implementation of the SEND syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| unsafe extern "C" fn sys_send_stub(_args: &mut SendArgs<'_>) -> RcLen { | |
| asm!(" | |
| @ Spill the registers we're about to use to pass stuff. | |
| push {{r4-r11}} | |
| @ Load in args from the struct. | |
| ldm r0, {{r4-r10}} | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ Move the two results back into their return positions. | |
| mov r0, r4 | |
| mov r1, r5 | |
| @ Restore the registers we used. | |
| pop {{r4-r11}} | |
| @ Fin. | |
| bx lr | |
| ", | |
| sysnum = const Sysnum::Send as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| /// Performs an "open" RECV that will accept messages from any task or | |
| /// notifications from the kernel. | |
| /// | |
| /// The next message sent to this task, or the highest priority message if | |
| /// several are pending simultaneously, will be written into `buffer`, and its | |
| /// information returned. | |
| /// | |
| /// `notification_mask` determines which notification bits can interrupt this | |
| /// RECV (any that are 1). If a notification interrupts the RECV, you will get a | |
| /// "message" originating from `TaskId::KERNEL`. | |
| /// | |
| /// This operation cannot fail -- it can be interrupted by a notification if you | |
| /// let it, but it always receives _something_. | |
| #[inline(always)] | |
| pub fn sys_recv_open(buffer: &mut [u8], notification_mask: u32) -> RecvMessage { | |
| // The open-receive version of the syscall is defined as being unable to | |
| // fail, and so we should always get a success here. (This is not using | |
| // `unwrap` because that generates handling code with formatting.) | |
| match sys_recv(buffer, notification_mask, None) { | |
| Ok(rm) => rm, | |
| Err(_) => panic!(), | |
| } | |
| } | |
| /// Performs a "closed" RECV that will only accept messages from `sender`. | |
| /// | |
| /// The next message sent from `sender` to this task (including a message that | |
| /// has already been sent, but is blocked) will be written into `buffer`, and | |
| /// its information returned. | |
| /// | |
| /// `notification_mask` determines which notification bits can interrupt this | |
| /// RECV (any that are 1). Note that, if `sender` is not `TaskId::KERNEL`, you | |
| /// can't actually receive any notifications with this operation, so | |
| /// `notification_mask` should always be zero in that case. | |
| /// | |
| /// If `sender` is stale (i.e. refers to a deceased generation of the task) when | |
| /// you call this, or if `sender` is rebooted while you're blocked in this | |
| /// operation, this will fail with `ClosedRecvError::Dead`. | |
| #[inline(always)] | |
| pub fn sys_recv_closed( | |
| buffer: &mut [u8], | |
| notification_mask: u32, | |
| sender: TaskId, | |
| ) -> Result<RecvMessage, ClosedRecvError> { | |
| sys_recv(buffer, notification_mask, Some(sender)) | |
| .map_err(|_| ClosedRecvError::Dead) | |
| } | |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] | |
| pub enum ClosedRecvError { | |
| Dead, | |
| } | |
| /// General version of RECV that lets you pick closed vs. open receive at | |
| /// runtime. | |
| /// | |
| /// You almost always want `sys_recv_open` or `sys_recv_closed` instead. | |
| #[inline(always)] | |
| pub fn sys_recv( | |
| buffer: &mut [u8], | |
| notification_mask: u32, | |
| specific_sender: Option<TaskId>, | |
| ) -> Result<RecvMessage, u32> { | |
| use core::mem::MaybeUninit; | |
| // Flatten option into a packed u32. | |
| let specific_sender = specific_sender | |
| .map(|tid| (1u32 << 31) | u32::from(tid.0)) | |
| .unwrap_or(0); | |
| let mut out = MaybeUninit::<RawRecvMessage>::uninit(); | |
| let rc = unsafe { | |
| sys_recv_stub( | |
| buffer.as_mut_ptr(), | |
| buffer.len(), | |
| notification_mask, | |
| specific_sender, | |
| out.as_mut_ptr(), | |
| ) | |
| }; | |
| // Safety: stub fully initializes output struct. On failure, it might | |
| // initialize it with nonsense, but that's okay -- it's still initialized. | |
| let out = unsafe { out.assume_init() }; | |
| if rc == 0 { | |
| Ok(RecvMessage { | |
| sender: TaskId(out.sender as u16), | |
| operation: out.operation, | |
| message_len: out.message_len, | |
| response_capacity: out.response_capacity, | |
| lease_count: out.lease_count, | |
| }) | |
| } else { | |
| Err(rc) | |
| } | |
| } | |
| pub struct RecvMessage { | |
| pub sender: TaskId, | |
| pub operation: u32, | |
| pub message_len: usize, | |
| pub response_capacity: usize, | |
| pub lease_count: usize, | |
| } | |
| /// Core implementation of the RECV syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| #[must_use] | |
| unsafe extern "C" fn sys_recv_stub( | |
| _buffer_ptr: *mut u8, | |
| _buffer_len: usize, | |
| _notification_mask: u32, | |
| _specific_sender: u32, | |
| _out: *mut RawRecvMessage, | |
| ) -> u32 { | |
| asm!(" | |
| @ Spill the registers we're about to use to pass stuff. | |
| push {{r4-r11}} | |
| @ Move register arguments into their proper positions. | |
| mov r4, r0 | |
| mov r5, r1 | |
| mov r6, r2 | |
| mov r7, r3 | |
| @ Read output buffer pointer from stack into a register that | |
| @ is preserved during our syscall. Since we just pushed a | |
| @ bunch of stuff, we need to read *past* it. | |
| ldr r3, [sp, #(8 * 4)] | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ Move status flag (only used for closed receive) into return | |
| @ position | |
| mov r0, r4 | |
| @ Write all the results out into the raw output buffer. | |
| stm r3, {{r5-r9}} | |
| @ Restore the registers we used. | |
| pop {{r4-r11}} | |
| @ Fin. | |
| bx lr | |
| ", | |
| sysnum = const Sysnum::Recv as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| /// Duplicated version of `RecvMessage` with all 32-bit fields and predictable | |
| /// field order, so that it can be generated from assembly. | |
| /// | |
| /// TODO: might be able to merge this into actual `RecvMessage` with some care. | |
| #[repr(C)] | |
| struct RawRecvMessage { | |
| pub sender: u32, | |
| pub operation: u32, | |
| pub message_len: usize, | |
| pub response_capacity: usize, | |
| pub lease_count: usize, | |
| } | |
| #[inline(always)] | |
| pub fn sys_reply(peer: TaskId, code: u32, message: &[u8]) { | |
| unsafe { | |
| sys_reply_stub(peer.0 as u32, code, message.as_ptr(), message.len()) | |
| } | |
| } | |
| /// Core implementation of the REPLY syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| unsafe extern "C" fn sys_reply_stub( | |
| _peer: u32, | |
| _code: u32, | |
| _message_ptr: *const u8, | |
| _message_len: usize, | |
| ) { | |
| asm!(" | |
| @ Spill the registers we're about to use to pass stuff. Note that we're | |
| @ being clever and pushing only the registers we need; this means the | |
| @ pop sequence at the end needs to match! (Why are we pushing LR? Because | |
| @ the ABI requires us to maintain 8-byte stack alignment, so we must | |
| @ push registers in pairs.) | |
| push {{r4-r7, r11, lr}} | |
| @ Move register arguments into place. | |
| mov r4, r0 | |
| mov r5, r1 | |
| mov r6, r2 | |
| mov r7, r3 | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ This call has no results. | |
| @ Restore the registers we used and return. | |
| pop {{r4-r7, r11, pc}} | |
| ", | |
| sysnum = const Sysnum::Reply as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| /// Sets this task's timer. | |
| /// | |
| /// The timer is set to `deadline`. If `deadline` is `None`, the timer is | |
| /// disabled. Otherwise, the timer is configured to notify when the specified | |
| /// time (in ticks since boot) is reached. When that occurs, the `notifications` | |
| /// will get posted to this task, and the timer will be disabled. | |
| /// | |
| /// If the deadline is chosen such that the timer *would have already fired*, | |
| /// had it been set earlier -- that is, if the deadline is `<=` the current time | |
| /// -- the `notifications` will be posted immediately and the timer will not be | |
| /// enabled. | |
| #[inline(always)] | |
| pub fn sys_set_timer(deadline: Option<u64>, notifications: u32) { | |
| let raw_deadline = deadline.unwrap_or(0); | |
| unsafe { | |
| sys_set_timer_stub( | |
| deadline.is_some() as u32, | |
| raw_deadline as u32, | |
| (raw_deadline >> 32) as u32, | |
| notifications, | |
| ) | |
| } | |
| } | |
| /// Core implementation of the SET_TIMER syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| unsafe extern "C" fn sys_set_timer_stub( | |
| _set_timer: u32, | |
| _deadline_lo: u32, | |
| _deadline_hi: u32, | |
| _notification: u32, | |
| ) { | |
| asm!(" | |
| @ Spill the registers we're about to use to pass stuff. Note that we're | |
| @ being clever and pushing only the registers we need; this means the | |
| @ pop sequence at the end needs to match! (Why are we pushing LR? Because | |
| @ the ABI requires us to maintain 8-byte stack alignment, so we must | |
| @ push registers in pairs.) | |
| push {{r4-r7, r11, lr}} | |
| @ Move register arguments into place. | |
| mov r4, r0 | |
| mov r5, r1 | |
| mov r6, r2 | |
| mov r7, r3 | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ This call has no results. | |
| @ Restore the registers we used and return. | |
| pop {{r4-r7, r11, pc}} | |
| ", | |
| sysnum = const Sysnum::SetTimer as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| #[inline(always)] | |
| pub fn sys_borrow_read( | |
| lender: TaskId, | |
| index: usize, | |
| offset: usize, | |
| dest: &mut [u8], | |
| ) -> (u32, usize) { | |
| let mut args = BorrowReadArgs { | |
| lender: lender.0 as u32, | |
| index, | |
| offset, | |
| dest: dest.as_mut_ptr(), | |
| dest_len: dest.len(), | |
| }; | |
| unsafe { sys_borrow_read_stub(&mut args).into() } | |
| } | |
| /// Core implementation of the BORROW_READ syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| unsafe extern "C" fn sys_borrow_read_stub(_args: *mut BorrowReadArgs) -> RcLen { | |
| asm!(" | |
| @ Spill the registers we're about to use to pass stuff. Note that we're | |
| @ being clever and pushing only the registers we need; this means the | |
| @ pop sequence at the end needs to match! | |
| push {{r4-r8, r11}} | |
| @ Move register arguments into place. | |
| ldm r0, {{r4-r8}} | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ Move the results into place. | |
| mov r0, r4 | |
| mov r1, r5 | |
| @ Restore the registers we used and return. | |
| pop {{r4-r8, r11}} | |
| bx lr | |
| ", | |
| sysnum = const Sysnum::BorrowRead as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| #[repr(C)] | |
| struct BorrowReadArgs { | |
| lender: u32, | |
| index: usize, | |
| offset: usize, | |
| dest: *mut u8, | |
| dest_len: usize, | |
| } | |
| #[inline(always)] | |
| pub fn sys_borrow_write( | |
| lender: TaskId, | |
| index: usize, | |
| offset: usize, | |
| src: &[u8], | |
| ) -> (u32, usize) { | |
| let mut args = BorrowWriteArgs { | |
| lender: lender.0 as u32, | |
| index, | |
| offset, | |
| src: src.as_ptr(), | |
| src_len: src.len(), | |
| }; | |
| unsafe { sys_borrow_write_stub(&mut args).into() } | |
| } | |
| /// Core implementation of the BORROW_WRITE syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| unsafe extern "C" fn sys_borrow_write_stub( | |
| _args: *mut BorrowWriteArgs, | |
| ) -> RcLen { | |
| asm!(" | |
| @ Spill the registers we're about to use to pass stuff. Note that we're | |
| @ being clever and pushing only the registers we need; this means the | |
| @ pop sequence at the end needs to match! | |
| push {{r4-r8, r11}} | |
| @ Move register arguments into place. | |
| ldm r0, {{r4-r8}} | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ Move the results into place. | |
| mov r0, r4 | |
| mov r1, r5 | |
| @ Restore the registers we used and return. | |
| pop {{r4-r8, r11}} | |
| bx lr | |
| ", | |
| sysnum = const Sysnum::BorrowWrite as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| #[repr(C)] | |
| struct BorrowWriteArgs { | |
| lender: u32, | |
| index: usize, | |
| offset: usize, | |
| src: *const u8, | |
| src_len: usize, | |
| } | |
| #[inline(always)] | |
| pub fn sys_borrow_info(lender: TaskId, index: usize) -> (u32, u32, usize) { | |
| use core::mem::MaybeUninit; | |
| let mut raw = MaybeUninit::<RawBorrowInfo>::uninit(); | |
| unsafe { | |
| sys_borrow_info_stub(lender.0 as u32, index, raw.as_mut_ptr()); | |
| } | |
| // Safety: stub completely initializes record | |
| let raw = unsafe { raw.assume_init() }; | |
| (raw.rc, raw.atts, raw.length) | |
| } | |
| #[repr(C)] | |
| struct RawBorrowInfo { | |
| rc: u32, | |
| atts: u32, | |
| length: usize, | |
| } | |
| /// Core implementation of the BORROW_INFO syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| unsafe extern "C" fn sys_borrow_info_stub( | |
| _lender: u32, | |
| _index: usize, | |
| _out: *mut RawBorrowInfo, | |
| ) { | |
| asm!(" | |
| @ Spill the registers we're about to use to pass stuff. Note that we're | |
| @ being clever and pushing only the registers we need; this means the | |
| @ pop sequence at the end needs to match! | |
| push {{r4-r6, r11}} | |
| @ Move register arguments into place. | |
| mov r4, r0 | |
| mov r5, r1 | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ Move the results into place. | |
| stm r2, {{r4-r6}} | |
| @ Restore the registers we used and return. | |
| pop {{r4-r6, r11}} | |
| bx lr | |
| ", | |
| sysnum = const Sysnum::BorrowInfo as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| #[inline(always)] | |
| pub fn sys_irq_control(mask: u32, enable: bool) { | |
| unsafe { | |
| sys_irq_control_stub(mask, enable as u32); | |
| } | |
| } | |
| /// Core implementation of the IRQ_CONTROL syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| unsafe extern "C" fn sys_irq_control_stub(_mask: u32, _enable: u32) { | |
| asm!(" | |
| @ Spill the registers we're about to use to pass stuff. Note that we're | |
| @ being clever and pushing only the registers we need; this means the | |
| @ pop sequence at the end needs to match! | |
| push {{r4, r5, r11, lr}} | |
| @ Move register arguments into place. | |
| mov r4, r0 | |
| mov r5, r1 | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ This call returns no results. | |
| @ Restore the registers we used and return. | |
| pop {{r4, r5, r11, pc}} | |
| ", | |
| sysnum = const Sysnum::IrqControl as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| #[inline(always)] | |
| pub fn sys_panic(msg: &[u8]) -> ! { | |
| unsafe { sys_panic_stub(msg.as_ptr(), msg.len()) } | |
| } | |
| /// Core implementation of the PANIC syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| unsafe extern "C" fn sys_panic_stub(_msg: *const u8, _len: usize) -> ! { | |
| asm!(" | |
| @ We're not going to return, so technically speaking we don't need to | |
| @ save registers. However, we save them anyway, so that we can reconstruct | |
| @ the state that led to the panic. | |
| push {{r4, r5, r11, lr}} | |
| @ Move register arguments into place. | |
| mov r4, r0 | |
| mov r5, r1 | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ This really shouldn't return. Ensure this: | |
| udf #0xad | |
| ", | |
| sysnum = const Sysnum::Panic as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| /// Reads the state of this task's timer. | |
| /// | |
| /// This returns three values in a `TimerState` struct: | |
| /// | |
| /// - `now` is the current time on the timer, in ticks since boot. | |
| /// - `deadline` is either `None`, meaning the timer notifications are disabled, | |
| /// or `Some(t)`, meaning the timer will post notifications at time `t`. | |
| /// - `on_dl` are the notification bits that will be posted on deadline. | |
| /// | |
| /// `deadline` and `on_dl` are as configured by `sys_set_timer`. | |
| /// | |
| /// `now` is monotonically advancing and can't be changed. | |
| #[inline(always)] | |
| pub fn sys_get_timer() -> TimerState { | |
| use core::mem::MaybeUninit; | |
| let mut out = MaybeUninit::<RawTimerState>::uninit(); | |
| unsafe { | |
| sys_get_timer_stub(out.as_mut_ptr()); | |
| } | |
| // Safety: stub fully initializes output struct. | |
| let out = unsafe { out.assume_init() }; | |
| TimerState { | |
| now: u64::from(out.now_lo) | u64::from(out.now_hi) << 32, | |
| deadline: if out.set != 0 { | |
| Some(u64::from(out.dl_lo) | u64::from(out.dl_hi) << 32) | |
| } else { | |
| None | |
| }, | |
| on_dl: out.on_dl, | |
| } | |
| } | |
| /// Result of `sys_get_timer`, provides information about task timer state. | |
| pub struct TimerState { | |
| /// Current task timer time, in ticks. | |
| pub now: u64, | |
| /// Current deadline, or `None` if the deadline is not pending. | |
| pub deadline: Option<u64>, | |
| /// Notifications to be delivered if the deadline is reached. | |
| pub on_dl: u32, | |
| } | |
| #[repr(C)] // loaded from assembly, field order must not change | |
| struct RawTimerState { | |
| now_lo: u32, | |
| now_hi: u32, | |
| set: u32, | |
| dl_lo: u32, | |
| dl_hi: u32, | |
| on_dl: u32, | |
| } | |
| /// Core implementation of the GET_TIMER syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| unsafe extern "C" fn sys_get_timer_stub(_out: *mut RawTimerState) { | |
| asm!(" | |
| @ Spill the registers we're about to use to pass stuff. | |
| push {{r4-r11}} | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ Write all the results out into the raw output buffer. | |
| stm r0, {{r4-r9}} | |
| @ Restore the registers we used. | |
| pop {{r4-r11}} | |
| @ Fin. | |
| bx lr | |
| ", | |
| sysnum = const Sysnum::GetTimer as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| /// This is the entry point for the kernel. Its job is to set up our memory | |
| /// before jumping to user-defined `main`. | |
| #[doc(hidden)] | |
| #[no_mangle] | |
| #[link_section = ".text.start"] | |
| #[naked] | |
| pub unsafe extern "C" fn _start() -> ! { | |
| // Provided by the user program: | |
| extern "Rust" { | |
| fn main() -> !; | |
| } | |
| asm!(" | |
| @ Copy data initialization image into data section. | |
| @ Note: this assumes that both source and destination are 32-bit | |
| @ aligned and padded to 4-byte boundary. | |
| movw r0, #:lower16:__edata @ upper bound in r0 | |
| movt r0, #:upper16:__edata | |
| movw r1, #:lower16:__sidata @ source in r1 | |
| movt r1, #:upper16:__sidata | |
| movw r2, #:lower16:__sdata @ dest in r2 | |
| movt r2, #:upper16:__sdata | |
| b 1f @ check for zero-sized data | |
| 2: ldr r3, [r1], #4 @ read and advance source | |
| str r3, [r2], #4 @ write and advance dest | |
| 1: cmp r2, r0 @ has dest reached the upper bound? | |
| bne 2b @ if not, repeat | |
| @ Zero BSS section. | |
| movw r0, #:lower16:__ebss @ upper bound in r0 | |
| movt r0, #:upper16:__ebss | |
| movw r1, #:lower16:__sbss @ base in r1 | |
| movt r1, #:upper16:__sbss | |
| movs r2, #0 @ materialize a zero | |
| b 1f @ check for zero-sized BSS | |
| 2: str r2, [r1], #4 @ zero one word and advance | |
| 1: cmp r1, r0 @ has base reached bound? | |
| bne 2b @ if not, repeat | |
| @ Be extra careful to ensure that those side effects are | |
| @ visible to the user program. | |
| dsb @ complete all writes | |
| isb @ and flush the pipeline | |
| @ Now, to the user entry point. We call it in case it | |
| @ returns. (It's not supposed to.) We reference it through | |
| @ a sym operand because it's a Rust func and may be mangled. | |
| bl {main} | |
| @ The noreturn option below will automatically generate an | |
| @ undefined instruction trap past this point, should main | |
| @ return. | |
| ", | |
| main = sym main, | |
| options(noreturn), | |
| ) | |
| } | |
| #[cfg(feature = "panic-messages")] | |
| #[panic_handler] | |
| fn panic(info: &core::panic::PanicInfo) -> ! { | |
| use core::fmt::Write; | |
| // Burn some stack to try to get at least the prefix of the panic info | |
| // recorded. | |
| struct PrefixWrite([u8; 128], usize); | |
| impl Write for PrefixWrite { | |
| fn write_str(&mut self, s: &str) -> core::fmt::Result { | |
| if self.1 < self.0.len() { | |
| let (_, remaining) = self.0.split_at_mut(self.1); | |
| let strbytes = s.as_bytes(); | |
| let to_write = remaining.len().min(strbytes.len()); | |
| remaining[..to_write].copy_from_slice(&strbytes[..to_write]); | |
| self.1 = self.1.wrapping_add(to_write); | |
| } | |
| Ok(()) | |
| } | |
| } | |
| let mut pw = PrefixWrite([0; 128], 0); | |
| write!(pw, "{}", info).ok(); | |
| sys_panic(if pw.1 < pw.0.len() { | |
| pw.0.split_at(pw.1).0 | |
| } else { | |
| &[] | |
| }); | |
| } | |
| #[cfg(not(feature = "panic-messages"))] | |
| #[panic_handler] | |
| fn panic(_: &core::panic::PanicInfo) -> ! { | |
| sys_panic(b"PANIC") | |
| } | |
| #[inline(always)] | |
| pub fn sys_refresh_task_id(task_id: TaskId) -> TaskId { | |
| let tid = unsafe { sys_refresh_task_id_stub(task_id.0 as u32) }; | |
| TaskId(tid as u16) | |
| } | |
| /// Core implementation of the REFRESH_TASK_ID syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| unsafe extern "C" fn sys_refresh_task_id_stub(_tid: u32) -> u32 { | |
| asm!(" | |
| @ Spill the registers we're about to use to pass stuff. Note that we're | |
| @ being clever and pushing only the registers we need (plus one to | |
| @ maintain alignment); this means the pop sequence at the end needs to | |
| @ match! | |
| push {{r4, r5, r11, lr}} | |
| @ Move register arguments into place. | |
| mov r4, r0 | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ Move result into place. | |
| mov r0, r4 | |
| @ Restore the registers we used and return. | |
| pop {{r4, r5, r11, pc}} | |
| ", | |
| sysnum = const Sysnum::RefreshTaskId as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| #[inline(always)] | |
| pub fn sys_post(task_id: TaskId, bits: u32) -> u32 { | |
| unsafe { sys_post_stub(task_id.0 as u32, bits) } | |
| } | |
| /// Core implementation of the POST syscall. | |
| /// | |
| /// See the note on syscall stubs at the top of this module for rationale. | |
| #[naked] | |
| unsafe extern "C" fn sys_post_stub(_tid: u32, _mask: u32) -> u32 { | |
| asm!(" | |
| @ Spill the registers we're about to use to pass stuff. Note that we're | |
| @ being clever and pushing only the registers we need; this means the | |
| @ pop sequence at the end needs to match! | |
| push {{r4, r5, r11, lr}} | |
| @ Move register arguments into place. | |
| mov r4, r0 | |
| mov r5, r1 | |
| @ Load the constant syscall number. | |
| mov r11, {sysnum} | |
| @ To the kernel! | |
| svc #0 | |
| @ Move result into place. | |
| mov r0, r4 | |
| @ Restore the registers we used and return. | |
| pop {{r4, r5, r11, pc}} | |
| ", | |
| sysnum = const Sysnum::Post as u32, | |
| options(noreturn), | |
| ) | |
| } | |
| // Enumeration of tasks in the application, for convenient reference, generated | |
| // by build.rs. | |
| // | |
| // The `Task` enum will contain one entry per task defined in the application, | |
| // with the value of that task's index. The `SELF` constant refers to the | |
| // current task. e.g. | |
| // | |
| // ``` | |
| // enum Task { | |
| // Init = 0, | |
| // Foo = 1, | |
| // Bar = 2, | |
| // } | |
| // | |
| // pub const SELF: Task = Task::Foo; | |
| // ``` | |
| // | |
| // When building a single task outside the context of an application, there will | |
| // be exactly one "task" in the enum, called `anonymous`. | |
| include!(concat!(env!("OUT_DIR"), "/tasks.rs")); |