about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSean Cross <sean@xobs.io>2022-11-07 11:06:38 +0800
committerSean Cross <sean@xobs.io>2023-08-22 20:25:38 +0800
commit10dad67f89a320c315c6c758dffe0bee610cbbfa (patch)
tree7b265d834ff779a73289f8eb1e61762ff86fc11f
parentdfff5bf62f466fbf6f17f20dc847e8f98b943535 (diff)
downloadrust-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.rs3
-rw-r--r--library/std/src/os/xous/services.rs132
-rw-r--r--library/std/src/os/xous/services/log.rs63
-rw-r--r--library/std/src/os/xous/services/systime.rs28
-rw-r--r--library/std/src/os/xous/services/ticktimer.rs38
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
+}