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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
//! Service Manager
//!
//! Services are system processes running in the background which wait for
//! incoming requests. When a process wants to communicate with a service, it
//! first needs to get a handle to the named service, and then it can communicate
//! with the service via inter-process communication (each service has a name up
//! to 7 characters followed by a \0).
//!
//! Handles for services are retrieved from the service manager port, "sm:", and
//! are released via svcCloseHandle or when a process is terminated or crashes.
//!
//! Manager service "sm:m" allows the Process Manager to tell sm: about the
//! permissions of each process. By default, SM assumes a process has no
//! permissions, and as such cannot access any service. "sm:m" RegisterProcess
//! calls allows PM to tell the Service Manager about which services a certain
//! process is allowed to access or host.
//!
//! A Service is very similar to a kernel-managed Named Port: You can connect to
//! it, and it returns a ClientSession. The difference is that a Service handled
//! by "sm:" has an additional permission check done to ensure it isn't accessed
//! by an unprivileged process.
//! Service Manager

#![no_std]

// rustc warnings
#![warn(unused)]
#![warn(missing_debug_implementations)]
#![allow(unused_unsafe)]
#![allow(unreachable_code)]
#![allow(dead_code)]
#![cfg_attr(test, allow(unused_imports))]

// rustdoc warnings
#![warn(missing_docs)] // hopefully this will soon become deny(missing_docs)
#![deny(intra_doc_link_resolution_failure)]

#[macro_use]
extern crate sunrise_libuser as libuser;
extern crate alloc;


#[macro_use]
extern crate lazy_static;

use log::*;
use alloc::boxed::Box;
use crate::libuser::syscalls;
use crate::libuser::futures::{WaitableManager, WorkQueue};
use crate::libuser::ipc::server::managed_port_handler;
use crate::libuser::types::*;
use crate::libuser::error::Error;
use crate::libuser::error::SmError;
use crate::libuser::futures_rs::future::{FutureExt, FutureObj};
use crate::libuser::sm::IUserInterfaceAsync;
use crate::libuser::loop_future::{Loop, loop_fn};
use hashbrown::hash_map::{HashMap, Entry};
use spin::Mutex;

use crate::libuser::futures_rs as futures;

/// `sm:` service interface.
/// The main interface to the Service Manager. Clients can use it to connect to
/// or register new services (assuming they have the appropriate capabilities).
///
/// Make sure to call the `IUserInterface::initialize` method before using it.
#[derive(Debug, Default, Clone)]
struct UserInterface;

lazy_static! {
    /// Global mapping of Service Name -> ClientPort.
    static ref SERVICES: Mutex<HashMap<ServiceName, ClientPort>> = Mutex::new(HashMap::new());
    // TODO: Implement a futures-based condvar instead of using event for in-process eventing.
    // BODY: A futures-based condvar can easily be implemented entirely in userspace, without
    // BODY: the need for any kernel help. It would have a lot less overhead than using a kernel Event.
    /// Event signaled whenever a new service is registered. `get_service` waits on this event
    /// if the wanted service is not found, to try again when a new service comes up later.
    static ref SERVICES_EVENT: (WritableEvent, ReadableEvent) = {
        crate::libuser::syscalls::create_event().unwrap()
    };
}

/// Get the length of a service encoded as an u64.
#[allow(clippy::verbose_bit_mask)] // More readable this way...
fn get_service_length(servicename: u64) -> usize{
    for i in 0..8 {
        if (servicename >> (8*i)) & 0xFF == 0 {
            return i;
        }
    }
    8
}

/// Casts an &u64 into an &str.
///
/// # Panics
///
/// Panics if the bytes of the u64 don't match valid UTF-8.
// Clippy is stupid and doesn't realize I'm returning a ref to servicename.
#[allow(clippy::trivially_copy_pass_by_ref)]
fn get_service_str(servicename: &u64) -> Result<&str, core::str::Utf8Error> {
    // TODO: Maybe I should use &[u8] instead?
    let len = get_service_length(*servicename);
    unsafe {
        core::str::from_utf8(core::slice::from_raw_parts(servicename as *const u64 as *const u8, len))
    }
}

/// Helper type that makes a ServiceName displayable.
// TODO: Move sm::ServiceName inside sunrise_libuser::sm.
// BODY: Sm has a ServiceName transparent struct that allows easy displaying of
// BODY: u64-encoded service names. Ideally, ServiceName should be generated by
// BODY: swipc-gen, and libuser would inject a Display implementation in it.
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
struct ServiceName(u64);

impl core::fmt::Debug for ServiceName {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        if let Ok(s) = get_service_str(&self.0) {
            core::fmt::Debug::fmt(s, f)
        } else {
            let _ = write!(f, "ServiceName({:x})", self.0);
            Ok(())
        }
    }
}

impl core::fmt::Display for ServiceName {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        if let Ok(s) = get_service_str(&self.0) {
            core::fmt::Debug::fmt(s, f)
        } else {
            let _ = write!(f, "ServiceName({:x})", self.0);
            Ok(())
        }
    }
}

impl IUserInterfaceAsync for UserInterface {
    /// Initialize the UserInterface, acquiring the Pid of the remote
    /// process, which will then be used to validate the permissions of each
    /// calls.
    fn initialize(&mut self, _manager: WorkQueue<'static>, _pid: Pid) -> FutureObj<'_, Result<(), Error>> {
        FutureObj::new(Box::new(futures::future::ok(())))
    }

    /// Get a ClientSession to this service.
    //
    // Implementation note: There is a possibility of deadlocking in `port.connect()` here!
    //
    // Here's what can happen: Someone calls `register_service()`, but then never accepts on it.
    // The call to `connect()` will block waiting for an accepter, and freeze `sm:` forever.
    //
    // It gets worse when you consider the old way of registering: We would get a handle to `sm:`,
    // register our handle, close the handle to `sm:`, and finally accept. The problem is, closing
    // the handle requires `sm:` to handle it, but by the time `sm:` gets there, it might already be
    // trying to connect to the port!
    //
    // For this reason, it is recommended for processes to use a global `sm:` handle.
    fn get_service<'a>(&mut self, work_queue: WorkQueue<'a>, servicename: u64) -> FutureObj<'a, Result<ClientSession, Error>> {
        let servicename = ServiceName(servicename);
        FutureObj::new(Box::new(loop_fn(work_queue, move |work_queue| {
            if let Some(port) = SERVICES.lock().get(&servicename) {
                debug!("Acquired service {}!", servicename);
                // Synchronous connect. This can block.
                let client = port.connect();
                futures::future::ready(Loop::Break(client)).left_future()
            } else {
                debug!("Service {} not currently registered. Sleeping.", servicename);
                SERVICES_EVENT.1.wait_async_cb(work_queue.clone(), move || {
                    if SERVICES.lock().contains_key(&servicename) { Some(()) }
                    else { None }
                })
                    .map(|_| {
                        Loop::Continue(work_queue)
                    }).right_future()
            }
        })))
    }
    /// Register a new service, returning a ServerPort to the newly
    /// registered service.
    fn register_service(&mut self, _work_queue: WorkQueue<'static>, servicename: u64, is_light: bool, max_handles: u32) -> FutureObj<'_, Result<ServerPort, Error>> {
        let servicename = ServiceName(servicename);

        let serverport = {
            let mut services_lock = SERVICES.lock();
            let entry = match services_lock.entry(servicename) {
                Entry::Occupied(_) => return FutureObj::new(Box::new(futures::future::err(SmError::ServiceAlreadyRegistered.into()))),
                Entry::Vacant(vacant) => vacant,
            };

            let (clientport, serverport) = match syscalls::create_port(max_handles, is_light, &servicename.0.to_ne_bytes()) {
                Ok(v) => v,
                Err(err) => return FutureObj::new(Box::new(futures::future::err(err.into())))
            };

            entry.insert(clientport);

            serverport
        };

        // Wake up potential get_service.
        SERVICES_EVENT.0.signal().unwrap();

        FutureObj::new(Box::new(futures::future::ok(serverport)))
    }

    /// Unregister a service.
    fn unregister_service(&mut self, _work_queue: WorkQueue<'static>, servicename: u64) -> FutureObj<'_, Result<(), Error>> {
        let servicename = ServiceName(servicename);
        match SERVICES.lock().remove(&servicename) {
            Some(_) => FutureObj::new(Box::new(futures::future::ok(()))),
            None => FutureObj::new(Box::new(futures::future::err(SmError::ServiceNotRegistered.into())))
        }
    }
}

fn main() {
    let mut man = WaitableManager::new();
    let handler = managed_port_handler(man.work_queue(), "sm:\0", UserInterface::dispatch).unwrap();

    man.work_queue().spawn(FutureObj::new(Box::new(handler)));

    man.run();
}

kip_header!(HEADER = sunrise_libuser::caps::KipHeader {
    magic: *b"KIP1",
    name: *b"sm\0\0\0\0\0\0\0\0\0\0",
    title_id: 0x0200000000000004,
    process_category: sunrise_libuser::caps::ProcessCategory::KernelBuiltin,
    main_thread_priority: 0,
    default_cpu_core: 0,
    flags: 0,
    reserved: 0,
    stack_page_count: 16,
});

capabilities!(CAPABILITIES = Capabilities {
    svcs: [
        sunrise_libuser::syscalls::nr::SleepThread,
        sunrise_libuser::syscalls::nr::ExitProcess,
        sunrise_libuser::syscalls::nr::CloseHandle,
        sunrise_libuser::syscalls::nr::WaitSynchronization,
        sunrise_libuser::syscalls::nr::OutputDebugString,
        sunrise_libuser::syscalls::nr::SetThreadArea,
        sunrise_libuser::syscalls::nr::QueryMemory,

        sunrise_libuser::syscalls::nr::SetHeapSize,
        sunrise_libuser::syscalls::nr::ManageNamedPort,
        sunrise_libuser::syscalls::nr::AcceptSession,
        sunrise_libuser::syscalls::nr::ReplyAndReceiveWithUserBuffer,
        sunrise_libuser::syscalls::nr::CreatePort,
        sunrise_libuser::syscalls::nr::ConnectToPort,
        sunrise_libuser::syscalls::nr::CreateEvent,
        sunrise_libuser::syscalls::nr::SignalEvent,
        sunrise_libuser::syscalls::nr::ClearEvent,
        sunrise_libuser::syscalls::nr::ResetSignal,
    ]
});