diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
commit | c23a457e72abe608715ac76f076f47dc42af07a5 (patch) | |
tree | 2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /library/std/src/os/xous/ffi.rs | |
parent | Releasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-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.rs | 647 |
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) + } +} |