diff options
| author | Sean Cross <sean@xobs.io> | 2022-11-07 11:06:38 +0800 |
|---|---|---|
| committer | Sean Cross <sean@xobs.io> | 2023-08-22 20:25:38 +0800 |
| commit | 10dad67f89a320c315c6c758dffe0bee610cbbfa (patch) | |
| tree | 7b265d834ff779a73289f8eb1e61762ff86fc11f | |
| parent | dfff5bf62f466fbf6f17f20dc847e8f98b943535 (diff) | |
| download | rust-10dad67f89a320c315c6c758dffe0bee610cbbfa.tar.gz rust-10dad67f89a320c315c6c758dffe0bee610cbbfa.zip | |
std: xous: add services support
Xous has a concept of `services` that provide various features. Processes may connect to these services by name or by address. Most services require a name server in order to connect. Add a file with the most common services, and provide a way to connect to a service by querying the name server. Signed-off-by: Sean Cross <sean@xobs.io>
| -rw-r--r-- | library/std/src/os/xous/mod.rs | 3 | ||||
| -rw-r--r-- | library/std/src/os/xous/services.rs | 132 | ||||
| -rw-r--r-- | library/std/src/os/xous/services/log.rs | 63 | ||||
| -rw-r--r-- | library/std/src/os/xous/services/systime.rs | 28 | ||||
| -rw-r--r-- | library/std/src/os/xous/services/ticktimer.rs | 38 |
5 files changed, 264 insertions, 0 deletions
diff --git a/library/std/src/os/xous/mod.rs b/library/std/src/os/xous/mod.rs index 3d69bc2e04e..153694a89a7 100644 --- a/library/std/src/os/xous/mod.rs +++ b/library/std/src/os/xous/mod.rs @@ -3,6 +3,9 @@ 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. diff --git a/library/std/src/os/xous/services.rs b/library/std/src/os/xous/services.rs new file mode 100644 index 00000000000..5c219f1fbb9 --- /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 00000000000..e6bae929eac --- /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 00000000000..bbb875c6942 --- /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 00000000000..447f8f9304a --- /dev/null +++ b/library/std/src/os/xous/services/ticktimer.rs @@ -0,0 +1,38 @@ +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 */), +} + +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], + } + } +} + +/// 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 +} |
