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 | |
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')
28 files changed, 2105 insertions, 42 deletions
diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 2180d2974..81106d6c6 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -15,8 +15,9 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// A borrowed file descriptor. /// -/// This has a lifetime parameter to tie it to the lifetime of something that -/// owns the file descriptor. +/// This has a lifetime parameter to tie it to the lifetime of something that owns the file +/// descriptor. For the duration of that lifetime, it is guaranteed that nobody will close the file +/// descriptor. /// /// This uses `repr(transparent)` and has the representation of a host file /// descriptor, so it can be used in FFI in places where a file descriptor is @@ -42,7 +43,8 @@ pub struct BorrowedFd<'fd> { /// An owned file descriptor. /// -/// This closes the file descriptor on drop. +/// This closes the file descriptor on drop. It is guaranteed that nobody else will close the file +/// descriptor. /// /// This uses `repr(transparent)` and has the representation of a host file /// descriptor, so it can be used in FFI in places where a file descriptor is @@ -155,7 +157,9 @@ impl FromRawFd for OwnedFd { /// # Safety /// /// The resource pointed to by `fd` must be open and suitable for assuming - /// ownership. The resource must not require any cleanup other than `close`. + /// [ownership][io-safety]. The resource must not require any cleanup other than `close`. + /// + /// [io-safety]: io#io-safety #[inline] unsafe fn from_raw_fd(fd: RawFd) -> Self { assert_ne!(fd, u32::MAX as RawFd); diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 592e072ad..ef896ea95 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -84,7 +84,10 @@ pub trait FromRawFd { /// /// # Safety /// - /// The `fd` passed in must be a valid and open file descriptor. + /// The `fd` passed in must be an [owned file descriptor][io-safety]; + /// in particular, it must be open. + /// + /// [io-safety]: io#io-safety /// /// # Example /// diff --git a/library/std/src/os/fortanix_sgx/io.rs b/library/std/src/os/fortanix_sgx/io.rs index 7223ade68..7e57435b6 100644 --- a/library/std/src/os/fortanix_sgx/io.rs +++ b/library/std/src/os/fortanix_sgx/io.rs @@ -31,15 +31,22 @@ pub trait FromRawFd { /// Constructs a new instance of `Self` from the given raw file /// descriptor and metadata. /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the object goes out of scope. + /// This function is typically used to **consume ownership** of the + /// specified file descriptor. When used in this way, the returned object + /// will take responsibility for closing it when the object goes out of + /// scope. /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. + /// However, consuming ownership is not strictly required. Use a + /// [`From<OwnedFd>::from`] implementation for an API which strictly + /// consumes ownership. + /// + /// # Safety + /// + /// The `fd` passed in must be an [owned file descriptor][io-safety]; + /// in particular, it must be open. + // FIXME: say something about `metadata`. + /// + /// [io-safety]: io#io-safety #[unstable(feature = "sgx_platform", issue = "56975")] unsafe fn from_raw_fd(fd: RawFd, metadata: Self::Metadata) -> Self; } diff --git a/library/std/src/os/hurd/fs.rs b/library/std/src/os/hurd/fs.rs new file mode 100644 index 000000000..00ff1560f --- /dev/null +++ b/library/std/src/os/hurd/fs.rs @@ -0,0 +1,348 @@ +//! Hurd-specific extensions to primitives in the [`std::fs`] module. +//! +//! [`std::fs`]: crate::fs + +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Returns the device ID on which this file resides. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_dev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + /// Returns the inode number. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ino()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + /// Returns the file type and mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mode()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + /// Returns the number of hard links to file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_nlink()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + /// Returns the user ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_uid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + /// Returns the group ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_gid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + /// Returns the device ID that this file represents. Only relevant for special file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_rdev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes. + /// + /// The size of a symbolic link is the length of the pathname it contains, + /// without a terminating null byte. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_size()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + /// Returns the last access time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + /// Returns the last access time of the file, in nanoseconds since [`st_atime`]. + /// + /// [`st_atime`]: Self::st_atime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + /// Returns the last modification time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + /// Returns the last modification time of the file, in nanoseconds since [`st_mtime`]. + /// + /// [`st_mtime`]: Self::st_mtime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + /// Returns the last status change time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + /// Returns the last status change time of the file, in nanoseconds since [`st_ctime`]. + /// + /// [`st_ctime`]: Self::st_ctime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + /// Returns the "preferred" block size for efficient filesystem I/O. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blksize()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + /// Returns the number of blocks allocated to the file, 512-byte units. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::hurd::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blocks()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_fsid as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_sec as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_sec as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_sec as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/library/std/src/os/hurd/mod.rs b/library/std/src/os/hurd/mod.rs new file mode 100644 index 000000000..aee86c7f6 --- /dev/null +++ b/library/std/src/os/hurd/mod.rs @@ -0,0 +1,6 @@ +//! Hurd-specific definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod fs; +pub mod raw; diff --git a/library/std/src/os/hurd/raw.rs b/library/std/src/os/hurd/raw.rs new file mode 100644 index 000000000..fa2666635 --- /dev/null +++ b/library/std/src/os/hurd/raw.rs @@ -0,0 +1,33 @@ +//! Hurd-specific raw type definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![deprecated( + since = "1.8.0", + note = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#![allow(deprecated)] + +use crate::os::raw::{c_long, c_uint, c_ulong}; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blkcnt_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blksize_t = c_long; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = c_ulong; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type ino_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = c_uint; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type nlink_t = c_ulong; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type off_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type time_t = c_long; + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = c_long; diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 634c3cc4a..11ad21515 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -117,6 +117,8 @@ pub mod haiku; pub mod hermit; #[cfg(target_os = "horizon")] pub mod horizon; +#[cfg(target_os = "hurd")] +pub mod hurd; #[cfg(target_os = "illumos")] pub mod illumos; #[cfg(target_os = "ios")] @@ -140,12 +142,16 @@ pub mod solid; #[cfg(target_os = "tvos")] #[path = "ios/mod.rs"] pub(crate) mod tvos; +#[cfg(target_os = "uefi")] +pub mod uefi; #[cfg(target_os = "vita")] pub mod vita; #[cfg(target_os = "vxworks")] pub mod vxworks; #[cfg(target_os = "watchos")] pub(crate) mod watchos; +#[cfg(target_os = "xous")] +pub mod xous; #[cfg(any(unix, target_os = "wasi", doc))] pub mod fd; diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index 33cc5a015..f82034663 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -27,15 +27,21 @@ pub trait FromRawFd { /// Constructs a new instance of `Self` from the given raw file /// descriptor. /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the object goes out of scope. + /// This function is typically used to **consume ownership** of the + /// specified file descriptor. When used in this way, the returned object + /// will take responsibility for closing it when the object goes out of + /// scope. /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. + /// However, consuming ownership is not strictly required. Use a + /// [`From<OwnedFd>::from`] implementation for an API which strictly + /// consumes ownership. + /// + /// # Safety + /// + /// The `fd` passed in must be an [owned file descriptor][io-safety]; + /// in particular, it must be open. + /// + /// [io-safety]: io#io-safety unsafe fn from_raw_fd(fd: RawFd) -> Self; } diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs new file mode 100644 index 000000000..5d082e7c1 --- /dev/null +++ b/library/std/src/os/uefi/env.rs @@ -0,0 +1,92 @@ +//! UEFI-specific extensions to the primitives in `std::env` module + +#![unstable(feature = "uefi_std", issue = "100499")] + +use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use crate::{ffi::c_void, ptr::NonNull}; + +static SYSTEM_TABLE: AtomicPtr<c_void> = AtomicPtr::new(crate::ptr::null_mut()); +static IMAGE_HANDLE: AtomicPtr<c_void> = AtomicPtr::new(crate::ptr::null_mut()); +// Flag to check if BootServices are still valid. +// Start with assuming that they are not available +static BOOT_SERVICES_FLAG: AtomicBool = AtomicBool::new(false); + +/// Initializes the global System Table and Image Handle pointers. +/// +/// The standard library requires access to the UEFI System Table and the Application Image Handle +/// to operate. Those are provided to UEFI Applications via their application entry point. By +/// calling `init_globals()`, those pointers are retained by the standard library for future use. +/// Thus this function must be called before any of the standard library services are used. +/// +/// The pointers are never exposed to any entity outside of this application and it is guaranteed +/// that, once the application exited, these pointers are never dereferenced again. +/// +/// Callers are required to ensure the pointers are valid for the entire lifetime of this +/// application. In particular, UEFI Boot Services must not be exited while an application with the +/// standard library is loaded. +/// +/// # SAFETY +/// Calling this function more than once will panic +pub(crate) unsafe fn init_globals(handle: NonNull<c_void>, system_table: NonNull<c_void>) { + IMAGE_HANDLE + .compare_exchange( + crate::ptr::null_mut(), + handle.as_ptr(), + Ordering::Release, + Ordering::Acquire, + ) + .unwrap(); + SYSTEM_TABLE + .compare_exchange( + crate::ptr::null_mut(), + system_table.as_ptr(), + Ordering::Release, + Ordering::Acquire, + ) + .unwrap(); + BOOT_SERVICES_FLAG.store(true, Ordering::Release) +} + +/// Get the SystemTable Pointer. +/// If you want to use `BootServices` then please use [`boot_services`] as it performs some +/// additional checks. +/// +/// Note: This function panics if the System Table or Image Handle is not initialized +pub fn system_table() -> NonNull<c_void> { + try_system_table().unwrap() +} + +/// Get the ImageHandle Pointer. +/// +/// Note: This function panics if the System Table or Image Handle is not initialized +pub fn image_handle() -> NonNull<c_void> { + try_image_handle().unwrap() +} + +/// Get the BootServices Pointer. +/// This function also checks if `ExitBootServices` has already been called. +pub fn boot_services() -> Option<NonNull<c_void>> { + if BOOT_SERVICES_FLAG.load(Ordering::Acquire) { + let system_table: NonNull<r_efi::efi::SystemTable> = try_system_table()?.cast(); + let boot_services = unsafe { (*system_table.as_ptr()).boot_services }; + NonNull::new(boot_services).map(|x| x.cast()) + } else { + None + } +} + +/// Get the SystemTable Pointer. +/// This function is mostly intended for places where panic is not an option +pub(crate) fn try_system_table() -> Option<NonNull<c_void>> { + NonNull::new(SYSTEM_TABLE.load(Ordering::Acquire)) +} + +/// Get the SystemHandle Pointer. +/// This function is mostly intended for places where panicking is not an option +pub(crate) fn try_image_handle() -> Option<NonNull<c_void>> { + NonNull::new(IMAGE_HANDLE.load(Ordering::Acquire)) +} + +pub(crate) fn disable_boot_services() { + BOOT_SERVICES_FLAG.store(false, Ordering::Release) +} diff --git a/library/std/src/os/uefi/mod.rs b/library/std/src/os/uefi/mod.rs new file mode 100644 index 000000000..8ef05eee1 --- /dev/null +++ b/library/std/src/os/uefi/mod.rs @@ -0,0 +1,8 @@ +//! Platform-specific extensions to `std` for UEFI. + +#![unstable(feature = "uefi_std", issue = "100499")] +#![doc(cfg(target_os = "uefi"))] + +pub mod env; +#[path = "../windows/ffi.rs"] +pub mod ffi; diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 029de8fbf..0eb4e88cf 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -123,7 +123,7 @@ pub trait FileExt { buf = &mut tmp[n..]; offset += n as u64; } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } @@ -155,7 +155,7 @@ pub trait FileExt { /// flag fail to respect the offset parameter, always appending to the end /// of the file instead. /// - /// It is possible to inadvertantly set this flag, like in the example below. + /// It is possible to inadvertently set this flag, like in the example below. /// Therefore, it is important to be vigilant while changing options to mitigate /// unexpected behaviour. /// @@ -258,7 +258,7 @@ pub trait FileExt { buf = &buf[n..]; offset += n as u64 } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs index 25b5dbff1..827278f8b 100644 --- a/library/std/src/os/unix/io/mod.rs +++ b/library/std/src/os/unix/io/mod.rs @@ -6,7 +6,8 @@ //! //! This module provides three types for representing file descriptors, //! with different ownership properties: raw, borrowed, and owned, which are -//! analogous to types used for representing pointers: +//! analogous to types used for representing pointers. These types reflect concepts of [I/O +//! safety][io-safety] on Unix. //! //! | Type | Analogous to | //! | ------------------ | ------------ | @@ -17,8 +18,8 @@ //! Like raw pointers, `RawFd` values are primitive values. And in new code, //! they should be considered unsafe to do I/O on (analogous to dereferencing //! them). Rust did not always provide this guidance, so existing code in the -//! Rust ecosystem often doesn't mark `RawFd` usage as unsafe. Once the -//! `io_safety` feature is stable, libraries will be encouraged to migrate, +//! Rust ecosystem often doesn't mark `RawFd` usage as unsafe. +//! Libraries are encouraged to migrate, //! either by adding `unsafe` to APIs that dereference `RawFd` values, or by //! using to `BorrowedFd` or `OwnedFd` instead. //! @@ -54,6 +55,8 @@ //! Like boxes, `OwnedFd` values conceptually own the resource they point to, //! and free (close) it when they are dropped. //! +//! See the [`io` module docs][io-safety] for a general explanation of I/O safety. +//! //! ## `/proc/self/mem` and similar OS features //! //! Some platforms have special files, such as `/proc/self/mem`, which @@ -65,15 +68,16 @@ //! to be opened and read from or written must be `unsafe`. Rust's safety guarantees //! only cover what the program itself can do, and not what entities outside //! the program can do to it. `/proc/self/mem` is considered to be such an -//! external entity, along with debugging interfaces, and people with physical access to -//! the hardware. This is true even in cases where the program is controlling -//! the external entity. +//! external entity, along with `/proc/self/fd/*`, debugging interfaces, and people with physical +//! access to the hardware. This is true even in cases where the program is controlling the external +//! entity. //! //! If you desire to comprehensively prevent programs from reaching out and //! causing external entities to reach back in and violate memory safety, it's //! necessary to use *sandboxing*, which is outside the scope of `std`. //! //! [`BorrowedFd<'a>`]: crate::os::unix::io::BorrowedFd +//! [io-safety]: crate::io#io-safety #![stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 401ec1e7a..3724e90af 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -53,6 +53,8 @@ mod platform { pub use crate::os::haiku::*; #[cfg(target_os = "horizon")] pub use crate::os::horizon::*; + #[cfg(target_os = "hurd")] + pub use crate::os::hurd::*; #[cfg(target_os = "illumos")] pub use crate::os::illumos::*; #[cfg(target_os = "ios")] diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 3d4302e66..6a6af9efd 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -662,7 +662,7 @@ fn test_send_vectored_fds_unix_stream() { } } -#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] +#[cfg(any(target_os = "android", target_os = "linux"))] #[test] #[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_send_vectored_with_ancillary_to_unix_datagram() { diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 2b40b672d..ac5510304 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -434,6 +434,20 @@ impl From<crate::process::ChildStdin> for OwnedFd { } } +/// Create a `ChildStdin` from the provided `OwnedFd`. +/// +/// The provided file descriptor must point to a pipe +/// with the `CLOEXEC` flag set. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From<OwnedFd> for process::ChildStdin { + #[inline] + fn from(fd: OwnedFd) -> process::ChildStdin { + let fd = sys::fd::FileDesc::from_inner(fd); + let pipe = sys::pipe::AnonPipe::from_inner(fd); + process::ChildStdin::from_inner(pipe) + } +} + #[stable(feature = "io_safety", since = "1.63.0")] impl AsFd for crate::process::ChildStdout { #[inline] @@ -450,6 +464,20 @@ impl From<crate::process::ChildStdout> for OwnedFd { } } +/// Create a `ChildStdout` from the provided `OwnedFd`. +/// +/// The provided file descriptor must point to a pipe +/// with the `CLOEXEC` flag set. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From<OwnedFd> for process::ChildStdout { + #[inline] + fn from(fd: OwnedFd) -> process::ChildStdout { + let fd = sys::fd::FileDesc::from_inner(fd); + let pipe = sys::pipe::AnonPipe::from_inner(fd); + process::ChildStdout::from_inner(pipe) + } +} + #[stable(feature = "io_safety", since = "1.63.0")] impl AsFd for crate::process::ChildStderr { #[inline] @@ -466,6 +494,20 @@ impl From<crate::process::ChildStderr> for OwnedFd { } } +/// Create a `ChildStderr` from the provided `OwnedFd`. +/// +/// The provided file descriptor must point to a pipe +/// with the `CLOEXEC` flag set. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From<OwnedFd> for process::ChildStderr { + #[inline] + fn from(fd: OwnedFd) -> process::ChildStderr { + let fd = sys::fd::FileDesc::from_inner(fd); + let pipe = sys::pipe::AnonPipe::from_inner(fd); + process::ChildStderr::from_inner(pipe) + } +} + /// Returns the OS-assigned process identifier associated with this process's parent. #[must_use] #[stable(feature = "unix_ppid", since = "1.27.0")] diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 160c8f1ec..3da8c8355 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -82,7 +82,7 @@ pub trait FileExt { buf = &mut tmp[n..]; offset += n as u64; } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } @@ -162,7 +162,7 @@ pub trait FileExt { buf = &buf[n..]; offset += n as u64 } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } diff --git a/library/std/src/os/windows/io/mod.rs b/library/std/src/os/windows/io/mod.rs index e2a401fb6..3d3ae3878 100644 --- a/library/std/src/os/windows/io/mod.rs +++ b/library/std/src/os/windows/io/mod.rs @@ -6,7 +6,8 @@ //! //! This module provides three types for representing raw handles and sockets //! with different ownership properties: raw, borrowed, and owned, which are -//! analogous to types used for representing pointers: +//! analogous to types used for representing pointers. These types reflect concepts of [I/O +//! safety][io-safety] on Windows. //! //! | Type | Analogous to | //! | ---------------------- | ------------ | @@ -23,8 +24,8 @@ //! And in new code, they should be considered unsafe to do I/O on (analogous //! to dereferencing them). Rust did not always provide this guidance, so //! existing code in the Rust ecosystem often doesn't mark `RawHandle` and -//! `RawSocket` usage as unsafe. Once the `io_safety` feature is stable, -//! libraries will be encouraged to migrate, either by adding `unsafe` to APIs +//! `RawSocket` usage as unsafe. +//! Libraries are encouraged to migrate, either by adding `unsafe` to APIs //! that dereference `RawHandle` and `RawSocket` values, or by using to //! `BorrowedHandle`, `BorrowedSocket`, `OwnedHandle`, or `OwnedSocket`. //! @@ -45,8 +46,11 @@ //! Like boxes, `OwnedHandle` and `OwnedSocket` values conceptually own the //! resource they point to, and free (close) it when they are dropped. //! +//! See the [`io` module docs][io-safety] for a general explanation of I/O safety. +//! //! [`BorrowedHandle<'a>`]: crate::os::windows::io::BorrowedHandle //! [`BorrowedSocket<'a>`]: crate::os::windows::io::BorrowedSocket +//! [io-safety]: crate::io#io-safety #![stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 1759e2e7f..770583a9c 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -62,7 +62,7 @@ pub trait FromRawHandle { /// # Safety /// /// The `handle` passed in must: - /// - be a valid an open handle, + /// - be an [owned handle][io-safety]; in particular, it must be open. /// - be a handle for a resource that may be freed via [`CloseHandle`] /// (as opposed to `RegCloseKey` or other close functions). /// @@ -71,6 +71,7 @@ pub trait FromRawHandle { /// /// [`CloseHandle`]: https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 + /// [io-safety]: io#io-safety #[stable(feature = "from_raw_os", since = "1.1.0")] unsafe fn from_raw_handle(handle: RawHandle) -> Self; } @@ -207,10 +208,11 @@ pub trait FromRawSocket { /// # Safety /// /// The `socket` passed in must: - /// - be a valid an open socket, + /// - be an [owned socket][io-safety]; in particular, it must be open. /// - be a socket that may be freed via [`closesocket`]. /// /// [`closesocket`]: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-closesocket + /// [io-safety]: io#io-safety #[stable(feature = "from_raw_os", since = "1.1.0")] unsafe fn from_raw_socket(sock: RawSocket) -> Self; } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 6359835ca..c80b9e284 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -116,7 +116,7 @@ impl BorrowedSocket<'_> { let mut info = unsafe { mem::zeroed::<sys::c::WSAPROTOCOL_INFOW>() }; let result = unsafe { sys::c::WSADuplicateSocketW( - self.as_raw_socket(), + self.as_raw_socket() as sys::c::SOCKET, sys::c::GetCurrentProcessId(), &mut info, ) @@ -134,7 +134,7 @@ impl BorrowedSocket<'_> { }; if socket != sys::c::INVALID_SOCKET { - unsafe { Ok(OwnedSocket::from_raw_socket(socket)) } + unsafe { Ok(OwnedSocket::from_raw_socket(socket as RawSocket)) } } else { let error = unsafe { sys::c::WSAGetLastError() }; @@ -158,7 +158,7 @@ impl BorrowedSocket<'_> { } unsafe { - let socket = OwnedSocket::from_raw_socket(socket); + let socket = OwnedSocket::from_raw_socket(socket as RawSocket); socket.set_no_inherit()?; Ok(socket) } @@ -211,7 +211,7 @@ impl Drop for OwnedSocket { #[inline] fn drop(&mut self) { unsafe { - let _ = sys::c::closesocket(self.socket); + let _ = sys::c::closesocket(self.socket as sys::c::SOCKET); } } } diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 073168cf2..d00e79476 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -106,6 +106,45 @@ impl IntoRawHandle for process::ChildStderr { } } +/// Create a `ChildStdin` from the provided `OwnedHandle`. +/// +/// The provided handle must be asynchronous, as reading and +/// writing from and to it is implemented using asynchronous APIs. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From<OwnedHandle> for process::ChildStdin { + fn from(handle: OwnedHandle) -> process::ChildStdin { + let handle = sys::handle::Handle::from_inner(handle); + let pipe = sys::pipe::AnonPipe::from_inner(handle); + process::ChildStdin::from_inner(pipe) + } +} + +/// Create a `ChildStdout` from the provided `OwnedHandle`. +/// +/// The provided handle must be asynchronous, as reading and +/// writing from and to it is implemented using asynchronous APIs. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From<OwnedHandle> for process::ChildStdout { + fn from(handle: OwnedHandle) -> process::ChildStdout { + let handle = sys::handle::Handle::from_inner(handle); + let pipe = sys::pipe::AnonPipe::from_inner(handle); + process::ChildStdout::from_inner(pipe) + } +} + +/// Create a `ChildStderr` from the provided `OwnedHandle`. +/// +/// The provided handle must be asynchronous, as reading and +/// writing from and to it is implemented using asynchronous APIs. +#[stable(feature = "child_stream_from_fd", since = "1.74.0")] +impl From<OwnedHandle> for process::ChildStderr { + fn from(handle: OwnedHandle) -> process::ChildStderr { + let handle = sys::handle::Handle::from_inner(handle); + let pipe = sys::pipe::AnonPipe::from_inner(handle); + process::ChildStderr::from_inner(pipe) + } +} + /// Windows-specific extensions to [`process::ExitStatus`]. /// /// This trait is sealed: it cannot be implemented outside the standard library. @@ -192,6 +231,66 @@ pub trait CommandExt: Sealed { /// ``` #[unstable(feature = "windows_process_extensions_async_pipes", issue = "98289")] fn async_pipes(&mut self, always_async: bool) -> &mut process::Command; + + /// Sets a raw attribute on the command, providing extended configuration options for Windows processes. + /// + /// This method allows you to specify custom attributes for a child process on Windows systems using raw attribute values. + /// Raw attributes provide extended configurability for process creation, but their usage can be complex and potentially unsafe. + /// + /// The `attribute` parameter specifies the raw attribute to be set, while the `value` parameter holds the value associated with that attribute. + /// Please refer to the [`windows-rs`](https://microsoft.github.io/windows-docs-rs/doc/windows/) documentation or the [`Win32 API documentation`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute) for detailed information about available attributes and their meanings. + /// + /// # Note + /// + /// The maximum number of raw attributes is the value of [`u32::MAX`]. + /// If this limit is exceeded, the call to [`process::Command::spawn`] will return an `Error` indicating that the maximum number of attributes has been exceeded. + /// # Safety + /// + /// The usage of raw attributes is potentially unsafe and should be done with caution. Incorrect attribute values or improper configuration can lead to unexpected behavior or errors. + /// + /// # Example + /// + /// The following example demonstrates how to create a child process with a specific parent process ID using a raw attribute. + /// + /// ```rust + /// #![feature(windows_process_extensions_raw_attribute)] + /// use std::os::windows::{process::CommandExt, io::AsRawHandle}; + /// use std::process::Command; + /// + /// # struct ProcessDropGuard(std::process::Child); + /// # impl Drop for ProcessDropGuard { + /// # fn drop(&mut self) { + /// # let _ = self.0.kill(); + /// # } + /// # } + /// + /// let parent = Command::new("cmd").spawn()?; + /// + /// let mut child_cmd = Command::new("cmd"); + /// + /// const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000; + /// + /// unsafe { + /// child_cmd.raw_attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, parent.as_raw_handle() as isize); + /// } + /// # + /// # let parent = ProcessDropGuard(parent); + /// + /// let mut child = child_cmd.spawn()?; + /// + /// # child.kill()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// # Safety Note + /// + /// Remember that improper use of raw attributes can lead to undefined behavior or security vulnerabilities. Always consult the documentation and ensure proper attribute values are used. + #[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] + unsafe fn raw_attribute<T: Copy + Send + Sync + 'static>( + &mut self, + attribute: usize, + value: T, + ) -> &mut process::Command; } #[stable(feature = "windows_process_extensions", since = "1.16.0")] @@ -219,6 +318,15 @@ impl CommandExt for process::Command { let _ = always_async; self } + + unsafe fn raw_attribute<T: Copy + Send + Sync + 'static>( + &mut self, + attribute: usize, + value: T, + ) -> &mut process::Command { + self.as_inner_mut().raw_attribute(attribute, value); + self + } } #[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")] 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) + } +} diff --git a/library/std/src/os/xous/ffi/definitions.rs b/library/std/src/os/xous/ffi/definitions.rs new file mode 100644 index 000000000..345005bcc --- /dev/null +++ b/library/std/src/os/xous/ffi/definitions.rs @@ -0,0 +1,283 @@ +mod memoryflags; +pub(crate) use memoryflags::*; + +#[stable(feature = "rust1", since = "1.0.0")] +/// Indicates a particular syscall number as used by the Xous kernel. +#[derive(Copy, Clone)] +#[repr(usize)] +pub enum Syscall { + MapMemory = 2, + Yield = 3, + UpdateMemoryFlags = 12, + ReceiveMessage = 15, + SendMessage = 16, + Connect = 17, + CreateThread = 18, + UnmapMemory = 19, + ReturnMemory = 20, + TerminateProcess = 22, + TrySendMessage = 24, + TryConnect = 25, + GetThreadId = 32, + JoinThread = 36, + AdjustProcessLimit = 38, + ReturnScalar = 40, +} + +#[stable(feature = "rust1", since = "1.0.0")] +/// Copies of these invocation types here for when we're running +/// in environments without libxous. +#[derive(Copy, Clone)] +#[repr(usize)] +pub enum SyscallResult { + Ok = 0, + Error = 1, + MemoryRange = 3, + ConnectionId = 7, + Message = 9, + ThreadId = 10, + Scalar1 = 14, + Scalar2 = 15, + MemoryReturned = 18, + Scalar5 = 20, +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Copy, Clone)] +/// A list of all known errors that may be returned by the Xous kernel. +#[repr(usize)] +pub enum Error { + NoError = 0, + BadAlignment = 1, + BadAddress = 2, + OutOfMemory = 3, + MemoryInUse = 4, + InterruptNotFound = 5, + InterruptInUse = 6, + InvalidString = 7, + ServerExists = 8, + ServerNotFound = 9, + ProcessNotFound = 10, + ProcessNotChild = 11, + ProcessTerminated = 12, + Timeout = 13, + InternalError = 14, + ServerQueueFull = 15, + ThreadNotAvailable = 16, + UnhandledSyscall = 17, + InvalidSyscall = 18, + ShareViolation = 19, + InvalidThread = 20, + InvalidPid = 21, + UnknownError = 22, + AccessDenied = 23, + UseBeforeInit = 24, + DoubleFree = 25, + DebugInProgress = 26, + InvalidLimit = 27, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From<usize> for Error { + fn from(src: usize) -> Self { + match src { + 0 => Self::NoError, + 1 => Self::BadAlignment, + 2 => Self::BadAddress, + 3 => Self::OutOfMemory, + 4 => Self::MemoryInUse, + 5 => Self::InterruptNotFound, + 6 => Self::InterruptInUse, + 7 => Self::InvalidString, + 8 => Self::ServerExists, + 9 => Self::ServerNotFound, + 10 => Self::ProcessNotFound, + 11 => Self::ProcessNotChild, + 12 => Self::ProcessTerminated, + 13 => Self::Timeout, + 14 => Self::InternalError, + 15 => Self::ServerQueueFull, + 16 => Self::ThreadNotAvailable, + 17 => Self::UnhandledSyscall, + 18 => Self::InvalidSyscall, + 19 => Self::ShareViolation, + 20 => Self::InvalidThread, + 21 => Self::InvalidPid, + 23 => Self::AccessDenied, + 24 => Self::UseBeforeInit, + 25 => Self::DoubleFree, + 26 => Self::DebugInProgress, + 27 => Self::InvalidLimit, + 22 | _ => Self::UnknownError, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From<i32> for Error { + fn from(src: i32) -> Self { + let Ok(src) = core::convert::TryInto::<usize>::try_into(src) else { + return Self::UnknownError; + }; + src.into() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "{}", + match self { + Error::NoError => "no error occurred", + Error::BadAlignment => "memory was not properly aligned", + Error::BadAddress => "an invalid address was supplied", + Error::OutOfMemory => "the process or service has run out of memory", + Error::MemoryInUse => "the requested address is in use", + Error::InterruptNotFound => + "the requested interrupt does not exist on this platform", + Error::InterruptInUse => "the requested interrupt is currently in use", + Error::InvalidString => "the specified string was not formatted correctly", + Error::ServerExists => "a server with that address already exists", + Error::ServerNotFound => "the requetsed server could not be found", + Error::ProcessNotFound => "the target process does not exist", + Error::ProcessNotChild => + "the requested operation can only be done on child processes", + Error::ProcessTerminated => "the target process has crashed", + Error::Timeout => "the requested operation timed out", + Error::InternalError => "an internal error occurred", + Error::ServerQueueFull => "the server has too many pending messages", + Error::ThreadNotAvailable => "the specified thread does not exist", + Error::UnhandledSyscall => "the kernel did not recognize that syscall", + Error::InvalidSyscall => "the syscall had incorrect parameters", + Error::ShareViolation => "an attempt was made to share memory twice", + Error::InvalidThread => "tried to resume a thread that was not ready", + Error::InvalidPid => "kernel attempted to use a pid that was not valid", + Error::AccessDenied => "no permission to perform the requested operation", + Error::UseBeforeInit => "attempt to use a service before initialization finished", + Error::DoubleFree => "the requested resource was freed twice", + Error::DebugInProgress => "kernel attempted to activate a thread being debugged", + Error::InvalidLimit => "process attempted to adjust an invalid limit", + Error::UnknownError => "an unknown error occurred", + } + ) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::fmt::Debug for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl crate::error::Error for Error {} + +/// Indicates the type of Message that is sent when making a `SendMessage` syscall. +#[derive(Copy, Clone)] +#[repr(usize)] +pub(crate) enum InvokeType { + LendMut = 1, + Lend = 2, + Move = 3, + Scalar = 4, + BlockingScalar = 5, +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug, Copy, Clone)] +/// A representation of a connection to a Xous service. +pub struct Connection(u32); + +#[stable(feature = "rust1", since = "1.0.0")] +impl From<u32> for Connection { + fn from(src: u32) -> Connection { + Connection(src) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl TryFrom<usize> for Connection { + type Error = core::num::TryFromIntError; + fn try_from(src: usize) -> Result<Self, Self::Error> { + Ok(Connection(src.try_into()?)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Into<u32> for Connection { + fn into(self) -> u32 { + self.0 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl TryInto<usize> for Connection { + type Error = core::num::TryFromIntError; + fn try_into(self) -> Result<usize, Self::Error> { + self.0.try_into() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub enum ServerAddressError { + InvalidLength, +} + +#[stable(feature = "rust1", since = "1.0.0")] +pub struct ServerAddress([u32; 4]); + +#[stable(feature = "rust1", since = "1.0.0")] +impl TryFrom<&str> for ServerAddress { + type Error = ServerAddressError; + fn try_from(value: &str) -> Result<Self, Self::Error> { + let b = value.as_bytes(); + if b.len() == 0 || b.len() > 16 { + return Err(Self::Error::InvalidLength); + } + + let mut this_temp = [0u8; 16]; + for (dest, src) in this_temp.iter_mut().zip(b.iter()) { + *dest = *src; + } + + let mut this = [0u32; 4]; + for (dest, src) in this.iter_mut().zip(this_temp.chunks_exact(4)) { + *dest = u32::from_le_bytes(src.try_into().unwrap()); + } + Ok(ServerAddress(this)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Into<[u32; 4]> for ServerAddress { + fn into(self) -> [u32; 4] { + self.0 + } +} + +#[derive(Debug, Copy, Clone)] +pub(crate) struct ThreadId(usize); + +impl From<usize> for ThreadId { + fn from(src: usize) -> ThreadId { + ThreadId(src) + } +} + +impl Into<usize> for ThreadId { + fn into(self) -> usize { + self.0 + } +} + +#[derive(Copy, Clone)] +#[repr(usize)] +/// Limits that can be passed to `AdjustLimit` +pub(crate) enum Limits { + HeapMaximum = 1, + HeapSize = 2, +} diff --git a/library/std/src/os/xous/ffi/definitions/memoryflags.rs b/library/std/src/os/xous/ffi/definitions/memoryflags.rs new file mode 100644 index 000000000..af9de3cbf --- /dev/null +++ b/library/std/src/os/xous/ffi/definitions/memoryflags.rs @@ -0,0 +1,176 @@ +/// Flags to be passed to the MapMemory struct. +/// Note that it is an error to have memory be +/// writable and not readable. +#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct MemoryFlags { + bits: usize, +} + +impl MemoryFlags { + /// Free this memory + #[stable(feature = "rust1", since = "1.0.0")] + pub const FREE: Self = Self { bits: 0b0000_0000 }; + + /// Immediately allocate this memory. Otherwise it will + /// be demand-paged. This is implicitly set when `phys` + /// is not 0. + #[stable(feature = "rust1", since = "1.0.0")] + pub const RESERVE: Self = Self { bits: 0b0000_0001 }; + + /// Allow the CPU to read from this page. + #[stable(feature = "rust1", since = "1.0.0")] + pub const R: Self = Self { bits: 0b0000_0010 }; + + /// Allow the CPU to write to this page. + #[stable(feature = "rust1", since = "1.0.0")] + pub const W: Self = Self { bits: 0b0000_0100 }; + + /// Allow the CPU to execute from this page. + #[stable(feature = "rust1", since = "1.0.0")] + pub const X: Self = Self { bits: 0b0000_1000 }; + + #[stable(feature = "rust1", since = "1.0.0")] + pub fn bits(&self) -> usize { + self.bits + } + + #[stable(feature = "rust1", since = "1.0.0")] + pub fn from_bits(raw: usize) -> Option<MemoryFlags> { + if raw > 16 { None } else { Some(MemoryFlags { bits: raw }) } + } + + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.bits == 0 + } + + #[stable(feature = "rust1", since = "1.0.0")] + pub fn empty() -> MemoryFlags { + MemoryFlags { bits: 0 } + } + + #[stable(feature = "rust1", since = "1.0.0")] + pub fn all() -> MemoryFlags { + MemoryFlags { bits: 15 } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::fmt::Binary for MemoryFlags { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Binary::fmt(&self.bits, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::fmt::Octal for MemoryFlags { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Octal::fmt(&self.bits, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::fmt::LowerHex for MemoryFlags { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::LowerHex::fmt(&self.bits, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::fmt::UpperHex for MemoryFlags { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::UpperHex::fmt(&self.bits, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::ops::BitOr for MemoryFlags { + type Output = Self; + + /// Returns the union of the two sets of flags. + #[inline] + fn bitor(self, other: MemoryFlags) -> Self { + Self { bits: self.bits | other.bits } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::ops::BitOrAssign for MemoryFlags { + /// Adds the set of flags. + #[inline] + fn bitor_assign(&mut self, other: Self) { + self.bits |= other.bits; + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::ops::BitXor for MemoryFlags { + type Output = Self; + + /// Returns the left flags, but with all the right flags toggled. + #[inline] + fn bitxor(self, other: Self) -> Self { + Self { bits: self.bits ^ other.bits } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::ops::BitXorAssign for MemoryFlags { + /// Toggles the set of flags. + #[inline] + fn bitxor_assign(&mut self, other: Self) { + self.bits ^= other.bits; + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::ops::BitAnd for MemoryFlags { + type Output = Self; + + /// Returns the intersection between the two sets of flags. + #[inline] + fn bitand(self, other: Self) -> Self { + Self { bits: self.bits & other.bits } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::ops::BitAndAssign for MemoryFlags { + /// Disables all flags disabled in the set. + #[inline] + fn bitand_assign(&mut self, other: Self) { + self.bits &= other.bits; + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::ops::Sub for MemoryFlags { + type Output = Self; + + /// Returns the set difference of the two sets of flags. + #[inline] + fn sub(self, other: Self) -> Self { + Self { bits: self.bits & !other.bits } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::ops::SubAssign for MemoryFlags { + /// Disables all flags enabled in the set. + #[inline] + fn sub_assign(&mut self, other: Self) { + self.bits &= !other.bits; + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl core::ops::Not for MemoryFlags { + type Output = Self; + + /// Returns the complement of this set of flags. + #[inline] + fn not(self) -> Self { + Self { bits: !self.bits } & MemoryFlags { bits: 15 } + } +} diff --git a/library/std/src/os/xous/mod.rs b/library/std/src/os/xous/mod.rs new file mode 100644 index 000000000..153694a89 --- /dev/null +++ b/library/std/src/os/xous/mod.rs @@ -0,0 +1,17 @@ +#![stable(feature = "rust1", since = "1.0.0")] +#![doc(cfg(target_os = "xous"))] + +pub mod ffi; + +#[stable(feature = "rust1", since = "1.0.0")] +pub mod services; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; +} diff --git a/library/std/src/os/xous/services.rs b/library/std/src/os/xous/services.rs new file mode 100644 index 000000000..5c219f1fb --- /dev/null +++ b/library/std/src/os/xous/services.rs @@ -0,0 +1,132 @@ +use crate::os::xous::ffi::Connection; +use core::sync::atomic::{AtomicU32, Ordering}; + +mod log; +pub(crate) use log::*; + +mod systime; +pub(crate) use systime::*; + +mod ticktimer; +pub(crate) use ticktimer::*; + +mod ns { + const NAME_MAX_LENGTH: usize = 64; + use crate::os::xous::ffi::{lend_mut, Connection}; + // By making this repr(C), the layout of this struct becomes well-defined + // and no longer shifts around. + // By marking it as `align(4096)` we define that it will be page-aligned, + // meaning it can be sent between processes. We make sure to pad out the + // entire struct so that memory isn't leaked to the name server. + #[repr(C, align(4096))] + struct ConnectRequest { + data: [u8; 4096], + } + + impl ConnectRequest { + pub fn new(name: &str) -> Self { + let mut cr = ConnectRequest { data: [0u8; 4096] }; + let name_bytes = name.as_bytes(); + + // Copy the string into our backing store. + for (&src_byte, dest_byte) in name_bytes.iter().zip(&mut cr.data[0..NAME_MAX_LENGTH]) { + *dest_byte = src_byte; + } + + // Set the string length to the length of the passed-in String, + // or the maximum possible length. Which ever is smaller. + for (&src_byte, dest_byte) in (name.len().min(NAME_MAX_LENGTH) as u32) + .to_le_bytes() + .iter() + .zip(&mut cr.data[NAME_MAX_LENGTH..]) + { + *dest_byte = src_byte; + } + cr + } + } + + pub fn connect_with_name_impl(name: &str, blocking: bool) -> Option<Connection> { + let mut request = ConnectRequest::new(name); + let opcode = if blocking { + 6 /* BlockingConnect */ + } else { + 7 /* TryConnect */ + }; + let cid = if blocking { super::name_server() } else { super::try_name_server()? }; + + lend_mut(cid, opcode, &mut request.data, 0, name.len().min(NAME_MAX_LENGTH)) + .expect("unable to perform lookup"); + + // Read the result code back from the nameserver + let result = u32::from_le_bytes(request.data[0..4].try_into().unwrap()); + if result == 0 { + // If the result was successful, then the CID is stored in the next 4 bytes + Some(u32::from_le_bytes(request.data[4..8].try_into().unwrap()).into()) + } else { + None + } + } + + pub fn connect_with_name(name: &str) -> Option<Connection> { + connect_with_name_impl(name, true) + } + + pub fn try_connect_with_name(name: &str) -> Option<Connection> { + connect_with_name_impl(name, false) + } +} + +/// Attempt to connect to a server by name. If the server does not exist, this will +/// block until the server is created. +/// +/// Note that this is different from connecting to a server by address. Server +/// addresses are always 16 bytes long, whereas server names are arbitrary-length +/// strings up to 64 bytes in length. +#[stable(feature = "rust1", since = "1.0.0")] +pub fn connect(name: &str) -> Option<Connection> { + ns::connect_with_name(name) +} + +/// Attempt to connect to a server by name. If the server does not exist, this will +/// immediately return `None`. +/// +/// Note that this is different from connecting to a server by address. Server +/// addresses are always 16 bytes long, whereas server names are arbitrary-length +/// strings. +#[stable(feature = "rust1", since = "1.0.0")] +pub fn try_connect(name: &str) -> Option<Connection> { + ns::try_connect_with_name(name) +} + +static NAME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); + +/// Return a `Connection` to the name server. If the name server has not been started, +/// then this call will block until the name server has been started. The `Connection` +/// will be shared among all connections in a process, so it is safe to call this +/// multiple times. +pub(crate) fn name_server() -> Connection { + let cid = NAME_SERVER_CONNECTION.load(Ordering::Relaxed); + if cid != 0 { + return cid.into(); + } + + let cid = crate::os::xous::ffi::connect("xous-name-server".try_into().unwrap()).unwrap(); + NAME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed); + cid +} + +fn try_name_server() -> Option<Connection> { + let cid = NAME_SERVER_CONNECTION.load(Ordering::Relaxed); + if cid != 0 { + return Some(cid.into()); + } + + if let Ok(Some(cid)) = crate::os::xous::ffi::try_connect("xous-name-server".try_into().unwrap()) + { + NAME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed); + Some(cid) + } else { + None + } +} diff --git a/library/std/src/os/xous/services/log.rs b/library/std/src/os/xous/services/log.rs new file mode 100644 index 000000000..e6bae929e --- /dev/null +++ b/library/std/src/os/xous/services/log.rs @@ -0,0 +1,63 @@ +use crate::os::xous::ffi::Connection; +use core::sync::atomic::{AtomicU32, Ordering}; + +/// Group `usize` bytes into a `usize` and return it, beginning +/// from `offset` * sizeof(usize) bytes from the start. For example, +/// `group_or_null([1,2,3,4,5,6,7,8], 1)` on a 32-bit system will +/// return a usize with 5678 packed into it. +fn group_or_null(data: &[u8], offset: usize) -> usize { + let start = offset * core::mem::size_of::<usize>(); + let mut out_array = [0u8; core::mem::size_of::<usize>()]; + if start < data.len() { + for (dest, src) in out_array.iter_mut().zip(&data[start..]) { + *dest = *src; + } + } + usize::from_le_bytes(out_array) +} + +pub(crate) enum LogScalar<'a> { + /// A panic occurred, and a panic log is forthcoming + BeginPanic, + + /// Some number of bytes will be appended to the log message + AppendPanicMessage(&'a [u8]), +} + +impl<'a> Into<[usize; 5]> for LogScalar<'a> { + fn into(self) -> [usize; 5] { + match self { + LogScalar::BeginPanic => [1000, 0, 0, 0, 0], + LogScalar::AppendPanicMessage(c) => + // Text is grouped into 4x `usize` words. The id is 1100 plus + // the number of characters in this message. + // Ignore errors since we're already panicking. + { + [ + 1100 + c.len(), + group_or_null(&c, 0), + group_or_null(&c, 1), + group_or_null(&c, 2), + group_or_null(&c, 3), + ] + } + } + } +} + +/// Return a `Connection` to the log server, which is used for printing messages to +/// the console and reporting panics. If the log server has not yet started, this +/// will block until the server is running. It is safe to call this multiple times, +/// because the address is shared among all threads in a process. +pub(crate) fn log_server() -> Connection { + static LOG_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); + + let cid = LOG_SERVER_CONNECTION.load(Ordering::Relaxed); + if cid != 0 { + return cid.into(); + } + + let cid = crate::os::xous::ffi::connect("xous-log-server ".try_into().unwrap()).unwrap(); + LOG_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed); + cid +} diff --git a/library/std/src/os/xous/services/systime.rs b/library/std/src/os/xous/services/systime.rs new file mode 100644 index 000000000..bbb875c69 --- /dev/null +++ b/library/std/src/os/xous/services/systime.rs @@ -0,0 +1,28 @@ +use crate::os::xous::ffi::{connect, Connection}; +use core::sync::atomic::{AtomicU32, Ordering}; + +pub(crate) enum SystimeScalar { + GetUtcTimeMs, +} + +impl Into<[usize; 5]> for SystimeScalar { + fn into(self) -> [usize; 5] { + match self { + SystimeScalar::GetUtcTimeMs => [3, 0, 0, 0, 0], + } + } +} + +/// Return a `Connection` to the systime server. This server is used for reporting the +/// realtime clock. +pub(crate) fn systime_server() -> Connection { + static SYSTIME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); + let cid = SYSTIME_SERVER_CONNECTION.load(Ordering::Relaxed); + if cid != 0 { + return cid.into(); + } + + let cid = connect("timeserverpublic".try_into().unwrap()).unwrap(); + SYSTIME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed); + cid +} diff --git a/library/std/src/os/xous/services/ticktimer.rs b/library/std/src/os/xous/services/ticktimer.rs new file mode 100644 index 000000000..7759303fd --- /dev/null +++ b/library/std/src/os/xous/services/ticktimer.rs @@ -0,0 +1,42 @@ +use crate::os::xous::ffi::Connection; +use core::sync::atomic::{AtomicU32, Ordering}; + +pub(crate) enum TicktimerScalar { + ElapsedMs, + SleepMs(usize), + LockMutex(usize /* cookie */), + UnlockMutex(usize /* cookie */), + WaitForCondition(usize /* cookie */, usize /* timeout (ms) */), + NotifyCondition(usize /* cookie */, usize /* count */), + FreeMutex(usize /* cookie */), + FreeCondition(usize /* cookie */), +} + +impl Into<[usize; 5]> for TicktimerScalar { + fn into(self) -> [usize; 5] { + match self { + TicktimerScalar::ElapsedMs => [0, 0, 0, 0, 0], + TicktimerScalar::SleepMs(msecs) => [1, msecs, 0, 0, 0], + TicktimerScalar::LockMutex(cookie) => [6, cookie, 0, 0, 0], + TicktimerScalar::UnlockMutex(cookie) => [7, cookie, 0, 0, 0], + TicktimerScalar::WaitForCondition(cookie, timeout_ms) => [8, cookie, timeout_ms, 0, 0], + TicktimerScalar::NotifyCondition(cookie, count) => [9, cookie, count, 0, 0], + TicktimerScalar::FreeMutex(cookie) => [10, cookie, 0, 0, 0], + TicktimerScalar::FreeCondition(cookie) => [11, cookie, 0, 0, 0], + } + } +} + +/// Return a `Connection` to the ticktimer server. This server is used for synchronization +/// primitives such as sleep, Mutex, and Condvar. +pub(crate) fn ticktimer_server() -> Connection { + static TICKTIMER_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); + let cid = TICKTIMER_SERVER_CONNECTION.load(Ordering::Relaxed); + if cid != 0 { + return cid.into(); + } + + let cid = crate::os::xous::ffi::connect("ticktimer-server".try_into().unwrap()).unwrap(); + TICKTIMER_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed); + cid +} |