Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 10dad67

Browse files
committed
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>
1 parent dfff5bf commit 10dad67

File tree

5 files changed

+264
-0
lines changed

5 files changed

+264
-0
lines changed

library/std/src/os/xous/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
pub mod ffi;
55

6+
#[stable(feature = "rust1", since = "1.0.0")]
7+
pub mod services;
8+
69
/// A prelude for conveniently writing platform-specific code.
710
///
811
/// Includes all extension traits, and some important type definitions.

library/std/src/os/xous/services.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use crate::os::xous::ffi::Connection;
2+
use core::sync::atomic::{AtomicU32, Ordering};
3+
4+
mod log;
5+
pub(crate) use log::*;
6+
7+
mod systime;
8+
pub(crate) use systime::*;
9+
10+
mod ticktimer;
11+
pub(crate) use ticktimer::*;
12+
13+
mod ns {
14+
const NAME_MAX_LENGTH: usize = 64;
15+
use crate::os::xous::ffi::{lend_mut, Connection};
16+
// By making this repr(C), the layout of this struct becomes well-defined
17+
// and no longer shifts around.
18+
// By marking it as `align(4096)` we define that it will be page-aligned,
19+
// meaning it can be sent between processes. We make sure to pad out the
20+
// entire struct so that memory isn't leaked to the name server.
21+
#[repr(C, align(4096))]
22+
struct ConnectRequest {
23+
data: [u8; 4096],
24+
}
25+
26+
impl ConnectRequest {
27+
pub fn new(name: &str) -> Self {
28+
let mut cr = ConnectRequest { data: [0u8; 4096] };
29+
let name_bytes = name.as_bytes();
30+
31+
// Copy the string into our backing store.
32+
for (&src_byte, dest_byte) in name_bytes.iter().zip(&mut cr.data[0..NAME_MAX_LENGTH]) {
33+
*dest_byte = src_byte;
34+
}
35+
36+
// Set the string length to the length of the passed-in String,
37+
// or the maximum possible length. Which ever is smaller.
38+
for (&src_byte, dest_byte) in (name.len().min(NAME_MAX_LENGTH) as u32)
39+
.to_le_bytes()
40+
.iter()
41+
.zip(&mut cr.data[NAME_MAX_LENGTH..])
42+
{
43+
*dest_byte = src_byte;
44+
}
45+
cr
46+
}
47+
}
48+
49+
pub fn connect_with_name_impl(name: &str, blocking: bool) -> Option<Connection> {
50+
let mut request = ConnectRequest::new(name);
51+
let opcode = if blocking {
52+
6 /* BlockingConnect */
53+
} else {
54+
7 /* TryConnect */
55+
};
56+
let cid = if blocking { super::name_server() } else { super::try_name_server()? };
57+
58+
lend_mut(cid, opcode, &mut request.data, 0, name.len().min(NAME_MAX_LENGTH))
59+
.expect("unable to perform lookup");
60+
61+
// Read the result code back from the nameserver
62+
let result = u32::from_le_bytes(request.data[0..4].try_into().unwrap());
63+
if result == 0 {
64+
// If the result was successful, then the CID is stored in the next 4 bytes
65+
Some(u32::from_le_bytes(request.data[4..8].try_into().unwrap()).into())
66+
} else {
67+
None
68+
}
69+
}
70+
71+
pub fn connect_with_name(name: &str) -> Option<Connection> {
72+
connect_with_name_impl(name, true)
73+
}
74+
75+
pub fn try_connect_with_name(name: &str) -> Option<Connection> {
76+
connect_with_name_impl(name, false)
77+
}
78+
}
79+
80+
/// Attempt to connect to a server by name. If the server does not exist, this will
81+
/// block until the server is created.
82+
///
83+
/// Note that this is different from connecting to a server by address. Server
84+
/// addresses are always 16 bytes long, whereas server names are arbitrary-length
85+
/// strings up to 64 bytes in length.
86+
#[stable(feature = "rust1", since = "1.0.0")]
87+
pub fn connect(name: &str) -> Option<Connection> {
88+
ns::connect_with_name(name)
89+
}
90+
91+
/// Attempt to connect to a server by name. If the server does not exist, this will
92+
/// immediately return `None`.
93+
///
94+
/// Note that this is different from connecting to a server by address. Server
95+
/// addresses are always 16 bytes long, whereas server names are arbitrary-length
96+
/// strings.
97+
#[stable(feature = "rust1", since = "1.0.0")]
98+
pub fn try_connect(name: &str) -> Option<Connection> {
99+
ns::try_connect_with_name(name)
100+
}
101+
102+
static NAME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
103+
104+
/// Return a `Connection` to the name server. If the name server has not been started,
105+
/// then this call will block until the name server has been started. The `Connection`
106+
/// will be shared among all connections in a process, so it is safe to call this
107+
/// multiple times.
108+
pub(crate) fn name_server() -> Connection {
109+
let cid = NAME_SERVER_CONNECTION.load(Ordering::Relaxed);
110+
if cid != 0 {
111+
return cid.into();
112+
}
113+
114+
let cid = crate::os::xous::ffi::connect("xous-name-server".try_into().unwrap()).unwrap();
115+
NAME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
116+
cid
117+
}
118+
119+
fn try_name_server() -> Option<Connection> {
120+
let cid = NAME_SERVER_CONNECTION.load(Ordering::Relaxed);
121+
if cid != 0 {
122+
return Some(cid.into());
123+
}
124+
125+
if let Ok(Some(cid)) = crate::os::xous::ffi::try_connect("xous-name-server".try_into().unwrap())
126+
{
127+
NAME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
128+
Some(cid)
129+
} else {
130+
None
131+
}
132+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use crate::os::xous::ffi::Connection;
2+
use core::sync::atomic::{AtomicU32, Ordering};
3+
4+
/// Group `usize` bytes into a `usize` and return it, beginning
5+
/// from `offset` * sizeof(usize) bytes from the start. For example,
6+
/// `group_or_null([1,2,3,4,5,6,7,8], 1)` on a 32-bit system will
7+
/// return a usize with 5678 packed into it.
8+
fn group_or_null(data: &[u8], offset: usize) -> usize {
9+
let start = offset * core::mem::size_of::<usize>();
10+
let mut out_array = [0u8; core::mem::size_of::<usize>()];
11+
if start < data.len() {
12+
for (dest, src) in out_array.iter_mut().zip(&data[start..]) {
13+
*dest = *src;
14+
}
15+
}
16+
usize::from_le_bytes(out_array)
17+
}
18+
19+
pub(crate) enum LogScalar<'a> {
20+
/// A panic occurred, and a panic log is forthcoming
21+
BeginPanic,
22+
23+
/// Some number of bytes will be appended to the log message
24+
AppendPanicMessage(&'a [u8]),
25+
}
26+
27+
impl<'a> Into<[usize; 5]> for LogScalar<'a> {
28+
fn into(self) -> [usize; 5] {
29+
match self {
30+
LogScalar::BeginPanic => [1000, 0, 0, 0, 0],
31+
LogScalar::AppendPanicMessage(c) =>
32+
// Text is grouped into 4x `usize` words. The id is 1100 plus
33+
// the number of characters in this message.
34+
// Ignore errors since we're already panicking.
35+
{
36+
[
37+
1100 + c.len(),
38+
group_or_null(&c, 0),
39+
group_or_null(&c, 1),
40+
group_or_null(&c, 2),
41+
group_or_null(&c, 3),
42+
]
43+
}
44+
}
45+
}
46+
}
47+
48+
/// Return a `Connection` to the log server, which is used for printing messages to
49+
/// the console and reporting panics. If the log server has not yet started, this
50+
/// will block until the server is running. It is safe to call this multiple times,
51+
/// because the address is shared among all threads in a process.
52+
pub(crate) fn log_server() -> Connection {
53+
static LOG_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
54+
55+
let cid = LOG_SERVER_CONNECTION.load(Ordering::Relaxed);
56+
if cid != 0 {
57+
return cid.into();
58+
}
59+
60+
let cid = crate::os::xous::ffi::connect("xous-log-server ".try_into().unwrap()).unwrap();
61+
LOG_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
62+
cid
63+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use crate::os::xous::ffi::{connect, Connection};
2+
use core::sync::atomic::{AtomicU32, Ordering};
3+
4+
pub(crate) enum SystimeScalar {
5+
GetUtcTimeMs,
6+
}
7+
8+
impl Into<[usize; 5]> for SystimeScalar {
9+
fn into(self) -> [usize; 5] {
10+
match self {
11+
SystimeScalar::GetUtcTimeMs => [3, 0, 0, 0, 0],
12+
}
13+
}
14+
}
15+
16+
/// Return a `Connection` to the systime server. This server is used for reporting the
17+
/// realtime clock.
18+
pub(crate) fn systime_server() -> Connection {
19+
static SYSTIME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
20+
let cid = SYSTIME_SERVER_CONNECTION.load(Ordering::Relaxed);
21+
if cid != 0 {
22+
return cid.into();
23+
}
24+
25+
let cid = connect("timeserverpublic".try_into().unwrap()).unwrap();
26+
SYSTIME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
27+
cid
28+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use crate::os::xous::ffi::Connection;
2+
use core::sync::atomic::{AtomicU32, Ordering};
3+
4+
pub(crate) enum TicktimerScalar {
5+
ElapsedMs,
6+
SleepMs(usize),
7+
LockMutex(usize /* cookie */),
8+
UnlockMutex(usize /* cookie */),
9+
WaitForCondition(usize /* cookie */, usize /* timeout (ms) */),
10+
NotifyCondition(usize /* cookie */, usize /* count */),
11+
}
12+
13+
impl Into<[usize; 5]> for TicktimerScalar {
14+
fn into(self) -> [usize; 5] {
15+
match self {
16+
TicktimerScalar::ElapsedMs => [0, 0, 0, 0, 0],
17+
TicktimerScalar::SleepMs(msecs) => [1, msecs, 0, 0, 0],
18+
TicktimerScalar::LockMutex(cookie) => [6, cookie, 0, 0, 0],
19+
TicktimerScalar::UnlockMutex(cookie) => [7, cookie, 0, 0, 0],
20+
TicktimerScalar::WaitForCondition(cookie, timeout_ms) => [8, cookie, timeout_ms, 0, 0],
21+
TicktimerScalar::NotifyCondition(cookie, count) => [9, cookie, count, 0, 0],
22+
}
23+
}
24+
}
25+
26+
/// Return a `Connection` to the ticktimer server. This server is used for synchronization
27+
/// primitives such as sleep, Mutex, and Condvar.
28+
pub(crate) fn ticktimer_server() -> Connection {
29+
static TICKTIMER_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
30+
let cid = TICKTIMER_SERVER_CONNECTION.load(Ordering::Relaxed);
31+
if cid != 0 {
32+
return cid.into();
33+
}
34+
35+
let cid = crate::os::xous::ffi::connect("ticktimer-server".try_into().unwrap()).unwrap();
36+
TICKTIMER_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
37+
cid
38+
}

0 commit comments

Comments
 (0)