summaryrefslogtreecommitdiffstats
path: root/library/std/src/os/xous/services.rs
blob: 5c219f1fbb95e155bb428fa2fea2e7dd2ece17cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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
    }
}