summaryrefslogtreecommitdiffstats
path: root/library/std/src/os/xous/ffi.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
commitc23a457e72abe608715ac76f076f47dc42af07a5 (patch)
tree2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /library/std/src/os/xous/ffi.rs
parentReleasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz
rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std/src/os/xous/ffi.rs')
-rw-r--r--library/std/src/os/xous/ffi.rs647
1 files changed, 647 insertions, 0 deletions
diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs
new file mode 100644
index 000000000..8be7fbb10
--- /dev/null
+++ b/library/std/src/os/xous/ffi.rs
@@ -0,0 +1,647 @@
+#![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 mut a6 = arg1;
+ let mut 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((a6, a7))
+ } else if result == SyscallResult::Error as usize {
+ Err(a1.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)
+ }
+}
+
+/// Connect 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)
+}
+
+/// Attempt 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),
+ }
+}
+
+/// Terminate the current process and return 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!();
+}
+
+/// Suspend 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 => _,
+ )
+ };
+}
+
+/// Allocate 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 * core::mem::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::from_exposed_addr_mut::<T>(a1);
+ let len = a2 / core::mem::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)
+ }
+}
+
+/// Destroy 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();
+ 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)
+ }
+}
+
+/// Adjust 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();
+ 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)
+ }
+}
+
+/// Create 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)
+ }
+}
+
+/// Wait for the given thread to terminate and return 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)
+ }
+}
+
+/// Get 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)
+ }
+}
+
+/// Adjust 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::JoinThread 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)
+ }
+}