| #![allow(dead_code)] | |
| #![allow(unused_variables)] | |
| #![stable(feature = "rust1", since = "1.0.0")] | |
| #[path = "../unix/ffi/os_str.rs"] | |
| mod os_str; | |
| #[stable(feature = "rust1", since = "1.0.0")] | |
| pub use self::os_str::{OsStrExt, OsStringExt}; | |
| mod definitions; | |
| #[stable(feature = "rust1", since = "1.0.0")] | |
| pub use definitions::*; | |
| fn lend_mut_impl( | |
| connection: Connection, | |
| opcode: usize, | |
| data: &mut [u8], | |
| arg1: usize, | |
| arg2: usize, | |
| blocking: bool, | |
| ) -> Result<(usize, usize), Error> { | |
| let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize; | |
| let mut a1: usize = connection.try_into().unwrap(); | |
| let mut a2 = InvokeType::LendMut as usize; | |
| let a3 = opcode; | |
| let a4 = data.as_mut_ptr() as usize; | |
| let a5 = data.len(); | |
| let a6 = arg1; | |
| let a7 = arg2; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0, | |
| inlateout("a1") a1, | |
| inlateout("a2") a2, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| let result = a0; | |
| if result == SyscallResult::MemoryReturned as usize { | |
| Ok((a1, a2)) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(a1.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } | |
| pub(crate) fn lend_mut( | |
| connection: Connection, | |
| opcode: usize, | |
| data: &mut [u8], | |
| arg1: usize, | |
| arg2: usize, | |
| ) -> Result<(usize, usize), Error> { | |
| lend_mut_impl(connection, opcode, data, arg1, arg2, true) | |
| } | |
| pub(crate) fn try_lend_mut( | |
| connection: Connection, | |
| opcode: usize, | |
| data: &mut [u8], | |
| arg1: usize, | |
| arg2: usize, | |
| ) -> Result<(usize, usize), Error> { | |
| lend_mut_impl(connection, opcode, data, arg1, arg2, false) | |
| } | |
| fn lend_impl( | |
| connection: Connection, | |
| opcode: usize, | |
| data: &[u8], | |
| arg1: usize, | |
| arg2: usize, | |
| blocking: bool, | |
| ) -> Result<(usize, usize), Error> { | |
| let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize; | |
| let a1: usize = connection.try_into().unwrap(); | |
| let a2 = InvokeType::Lend as usize; | |
| let a3 = opcode; | |
| let a4 = data.as_ptr() as usize; | |
| let a5 = data.len(); | |
| let a6 = arg1; | |
| let a7 = arg2; | |
| let mut ret1; | |
| let mut ret2; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0, | |
| inlateout("a1") a1 => ret1, | |
| inlateout("a2") a2 => ret2, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| let result = a0; | |
| if result == SyscallResult::MemoryReturned as usize { | |
| Ok((ret1, ret2)) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(ret1.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } | |
| pub(crate) fn lend( | |
| connection: Connection, | |
| opcode: usize, | |
| data: &[u8], | |
| arg1: usize, | |
| arg2: usize, | |
| ) -> Result<(usize, usize), Error> { | |
| lend_impl(connection, opcode, data, arg1, arg2, true) | |
| } | |
| pub(crate) fn try_lend( | |
| connection: Connection, | |
| opcode: usize, | |
| data: &[u8], | |
| arg1: usize, | |
| arg2: usize, | |
| ) -> Result<(usize, usize), Error> { | |
| lend_impl(connection, opcode, data, arg1, arg2, false) | |
| } | |
| fn scalar_impl(connection: Connection, args: [usize; 5], blocking: bool) -> Result<(), Error> { | |
| let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize; | |
| let mut a1: usize = connection.try_into().unwrap(); | |
| let a2 = InvokeType::Scalar as usize; | |
| let a3 = args[0]; | |
| let a4 = args[1]; | |
| let a5 = args[2]; | |
| let a6 = args[3]; | |
| let a7 = args[4]; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0, | |
| inlateout("a1") a1, | |
| inlateout("a2") a2 => _, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| let result = a0; | |
| if result == SyscallResult::Ok as usize { | |
| Ok(()) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(a1.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } | |
| pub(crate) fn scalar(connection: Connection, args: [usize; 5]) -> Result<(), Error> { | |
| scalar_impl(connection, args, true) | |
| } | |
| pub(crate) fn try_scalar(connection: Connection, args: [usize; 5]) -> Result<(), Error> { | |
| scalar_impl(connection, args, false) | |
| } | |
| fn blocking_scalar_impl( | |
| connection: Connection, | |
| args: [usize; 5], | |
| blocking: bool, | |
| ) -> Result<[usize; 5], Error> { | |
| let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize; | |
| let mut a1: usize = connection.try_into().unwrap(); | |
| let mut a2 = InvokeType::BlockingScalar as usize; | |
| let mut a3 = args[0]; | |
| let mut a4 = args[1]; | |
| let mut a5 = args[2]; | |
| let a6 = args[3]; | |
| let a7 = args[4]; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0, | |
| inlateout("a1") a1, | |
| inlateout("a2") a2, | |
| inlateout("a3") a3, | |
| inlateout("a4") a4, | |
| inlateout("a5") a5, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| let result = a0; | |
| if result == SyscallResult::Scalar1 as usize { | |
| Ok([a1, 0, 0, 0, 0]) | |
| } else if result == SyscallResult::Scalar2 as usize { | |
| Ok([a1, a2, 0, 0, 0]) | |
| } else if result == SyscallResult::Scalar5 as usize { | |
| Ok([a1, a2, a3, a4, a5]) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(a1.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } | |
| pub(crate) fn blocking_scalar( | |
| connection: Connection, | |
| args: [usize; 5], | |
| ) -> Result<[usize; 5], Error> { | |
| blocking_scalar_impl(connection, args, true) | |
| } | |
| pub(crate) fn try_blocking_scalar( | |
| connection: Connection, | |
| args: [usize; 5], | |
| ) -> Result<[usize; 5], Error> { | |
| blocking_scalar_impl(connection, args, false) | |
| } | |
| fn connect_impl(address: ServerAddress, blocking: bool) -> Result<Connection, Error> { | |
| let a0 = if blocking { Syscall::Connect } else { Syscall::TryConnect } as usize; | |
| let address: [u32; 4] = address.into(); | |
| let a1: usize = address[0].try_into().unwrap(); | |
| let a2: usize = address[1].try_into().unwrap(); | |
| let a3: usize = address[2].try_into().unwrap(); | |
| let a4: usize = address[3].try_into().unwrap(); | |
| let a5 = 0; | |
| let a6 = 0; | |
| let a7 = 0; | |
| let mut result: usize; | |
| let mut value: usize; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0 => result, | |
| inlateout("a1") a1 => value, | |
| inlateout("a2") a2 => _, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| if result == SyscallResult::ConnectionId as usize { | |
| Ok(value.try_into().unwrap()) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(value.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } | |
| /// Connects to a Xous server represented by the specified `address`. | |
| /// | |
| /// The current thread will block until the server is available. Returns | |
| /// an error if the server cannot accept any more connections. | |
| pub(crate) fn connect(address: ServerAddress) -> Result<Connection, Error> { | |
| connect_impl(address, true) | |
| } | |
| /// Attempts to connect to a Xous server represented by the specified `address`. | |
| /// | |
| /// If the server does not exist then None is returned. | |
| pub(crate) fn try_connect(address: ServerAddress) -> Result<Option<Connection>, Error> { | |
| match connect_impl(address, false) { | |
| Ok(conn) => Ok(Some(conn)), | |
| Err(Error::ServerNotFound) => Ok(None), | |
| Err(e) => Err(e), | |
| } | |
| } | |
| /// Terminates the current process and returns the specified code to the parent process. | |
| pub(crate) fn exit(return_code: u32) -> ! { | |
| let a0 = Syscall::TerminateProcess as usize; | |
| let a1 = return_code as usize; | |
| let a2 = 0; | |
| let a3 = 0; | |
| let a4 = 0; | |
| let a5 = 0; | |
| let a6 = 0; | |
| let a7 = 0; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| in("a0") a0, | |
| in("a1") a1, | |
| in("a2") a2, | |
| in("a3") a3, | |
| in("a4") a4, | |
| in("a5") a5, | |
| in("a6") a6, | |
| in("a7") a7, | |
| ) | |
| }; | |
| unreachable!(); | |
| } | |
| /// Suspends the current thread and allow another thread to run. This thread may | |
| /// continue executing again immediately if there are no other threads available | |
| /// to run on the system. | |
| pub(crate) fn do_yield() { | |
| let a0 = Syscall::Yield as usize; | |
| let a1 = 0; | |
| let a2 = 0; | |
| let a3 = 0; | |
| let a4 = 0; | |
| let a5 = 0; | |
| let a6 = 0; | |
| let a7 = 0; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0 => _, | |
| inlateout("a1") a1 => _, | |
| inlateout("a2") a2 => _, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| } | |
| /// Allocates memory from the system. | |
| /// | |
| /// An optional physical and/or virtual address may be specified in order to | |
| /// ensure memory is allocated at specific offsets, otherwise the kernel will | |
| /// select an address. | |
| /// | |
| /// # Safety | |
| /// | |
| /// This function is safe unless a virtual address is specified. In that case, | |
| /// the kernel will return an alias to the existing range. This violates Rust's | |
| /// pointer uniqueness guarantee. | |
| pub(crate) unsafe fn map_memory<T>( | |
| phys: Option<core::ptr::NonNull<T>>, | |
| virt: Option<core::ptr::NonNull<T>>, | |
| count: usize, | |
| flags: MemoryFlags, | |
| ) -> Result<&'static mut [T], Error> { | |
| let mut a0 = Syscall::MapMemory as usize; | |
| let mut a1 = phys.map(|p| p.as_ptr() as usize).unwrap_or_default(); | |
| let mut a2 = virt.map(|p| p.as_ptr() as usize).unwrap_or_default(); | |
| let a3 = count * size_of::<T>(); | |
| let a4 = flags.bits(); | |
| let a5 = 0; | |
| let a6 = 0; | |
| let a7 = 0; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0, | |
| inlateout("a1") a1, | |
| inlateout("a2") a2, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| let result = a0; | |
| if result == SyscallResult::MemoryRange as usize { | |
| let start = core::ptr::with_exposed_provenance_mut::<T>(a1); | |
| let len = a2 / size_of::<T>(); | |
| let end = unsafe { start.add(len) }; | |
| Ok(unsafe { core::slice::from_raw_parts_mut(start, len) }) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(a1.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } | |
| /// Destroys the given memory, returning it to the compiler. | |
| /// | |
| /// Safety: The memory pointed to by `range` should not be used after this | |
| /// function returns, even if this function returns Err(). | |
| pub(crate) unsafe fn unmap_memory<T>(range: *mut [T]) -> Result<(), Error> { | |
| let mut a0 = Syscall::UnmapMemory as usize; | |
| let mut a1 = range.as_mut_ptr() as usize; | |
| let a2 = range.len() * size_of::<T>(); | |
| let a3 = 0; | |
| let a4 = 0; | |
| let a5 = 0; | |
| let a6 = 0; | |
| let a7 = 0; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0, | |
| inlateout("a1") a1, | |
| inlateout("a2") a2 => _, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| let result = a0; | |
| if result == SyscallResult::Ok as usize { | |
| Ok(()) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(a1.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } | |
| /// Adjusts the memory flags for the given range. | |
| /// | |
| /// This can be used to remove flags from a given region in order to harden | |
| /// memory access. Note that flags may only be removed and may never be added. | |
| /// | |
| /// Safety: The memory pointed to by `range` may become inaccessible or have its | |
| /// mutability removed. It is up to the caller to ensure that the flags specified | |
| /// by `new_flags` are upheld, otherwise the program will crash. | |
| pub(crate) unsafe fn update_memory_flags<T>( | |
| range: *mut [T], | |
| new_flags: MemoryFlags, | |
| ) -> Result<(), Error> { | |
| let mut a0 = Syscall::UpdateMemoryFlags as usize; | |
| let mut a1 = range.as_mut_ptr() as usize; | |
| let a2 = range.len() * size_of::<T>(); | |
| let a3 = new_flags.bits(); | |
| let a4 = 0; // Process ID is currently None | |
| let a5 = 0; | |
| let a6 = 0; | |
| let a7 = 0; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0, | |
| inlateout("a1") a1, | |
| inlateout("a2") a2 => _, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| let result = a0; | |
| if result == SyscallResult::Ok as usize { | |
| Ok(()) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(a1.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } | |
| /// Creates a thread with a given stack and up to four arguments. | |
| pub(crate) fn create_thread( | |
| start: *mut usize, | |
| stack: *mut [u8], | |
| arg0: usize, | |
| arg1: usize, | |
| arg2: usize, | |
| arg3: usize, | |
| ) -> Result<ThreadId, Error> { | |
| let mut a0 = Syscall::CreateThread as usize; | |
| let mut a1 = start as usize; | |
| let a2 = stack.as_mut_ptr() as usize; | |
| let a3 = stack.len(); | |
| let a4 = arg0; | |
| let a5 = arg1; | |
| let a6 = arg2; | |
| let a7 = arg3; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0, | |
| inlateout("a1") a1, | |
| inlateout("a2") a2 => _, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| let result = a0; | |
| if result == SyscallResult::ThreadId as usize { | |
| Ok(a1.into()) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(a1.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } | |
| /// Waits for the given thread to terminate and returns the exit code from that thread. | |
| pub(crate) fn join_thread(thread_id: ThreadId) -> Result<usize, Error> { | |
| let mut a0 = Syscall::JoinThread as usize; | |
| let mut a1 = thread_id.into(); | |
| let a2 = 0; | |
| let a3 = 0; | |
| let a4 = 0; | |
| let a5 = 0; | |
| let a6 = 0; | |
| let a7 = 0; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0, | |
| inlateout("a1") a1, | |
| inlateout("a2") a2 => _, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| let result = a0; | |
| if result == SyscallResult::Scalar1 as usize { | |
| Ok(a1) | |
| } else if result == SyscallResult::Scalar2 as usize { | |
| Ok(a1) | |
| } else if result == SyscallResult::Scalar5 as usize { | |
| Ok(a1) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(a1.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } | |
| /// Gets the current thread's ID. | |
| pub(crate) fn thread_id() -> Result<ThreadId, Error> { | |
| let mut a0 = Syscall::GetThreadId as usize; | |
| let mut a1 = 0; | |
| let a2 = 0; | |
| let a3 = 0; | |
| let a4 = 0; | |
| let a5 = 0; | |
| let a6 = 0; | |
| let a7 = 0; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0, | |
| inlateout("a1") a1, | |
| inlateout("a2") a2 => _, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| let result = a0; | |
| if result == SyscallResult::ThreadId as usize { | |
| Ok(a1.into()) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(a1.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } | |
| /// Adjusts the given `knob` limit to match the new value `new`. The current value must | |
| /// match the `current` in order for this to take effect. | |
| /// | |
| /// The new value is returned as a result of this call. If the call fails, then the old | |
| /// value is returned. In either case, this function returns successfully. | |
| /// | |
| /// An error is generated if the `knob` is not a valid limit, or if the call | |
| /// would not succeed. | |
| pub(crate) fn adjust_limit(knob: Limits, current: usize, new: usize) -> Result<usize, Error> { | |
| let mut a0 = Syscall::AdjustProcessLimit as usize; | |
| let mut a1 = knob as usize; | |
| let a2 = current; | |
| let a3 = new; | |
| let a4 = 0; | |
| let a5 = 0; | |
| let a6 = 0; | |
| let a7 = 0; | |
| unsafe { | |
| core::arch::asm!( | |
| "ecall", | |
| inlateout("a0") a0, | |
| inlateout("a1") a1, | |
| inlateout("a2") a2 => _, | |
| inlateout("a3") a3 => _, | |
| inlateout("a4") a4 => _, | |
| inlateout("a5") a5 => _, | |
| inlateout("a6") a6 => _, | |
| inlateout("a7") a7 => _, | |
| ) | |
| }; | |
| let result = a0; | |
| if result == SyscallResult::Scalar2 as usize && a1 == knob as usize { | |
| Ok(a2) | |
| } else if result == SyscallResult::Scalar5 as usize && a1 == knob as usize { | |
| Ok(a1) | |
| } else if result == SyscallResult::Error as usize { | |
| Err(a1.into()) | |
| } else { | |
| Err(Error::InternalError) | |
| } | |
| } |