moto_rt/
lib.rs

1//! Motor OS Runtime Library. It is a stub/proxy to Motor OS Runtime VDSO
2//! (Virtual Dynamic Shared Object) which is loaded into every userspace process.
3//!
4//! The Runtime API surface is explicitly designed to provide Rust standard
5//! library PAL (platform abstraction layer); while it may evolve later into
6//! a more universal Runtime API (e.g. to be used in Go runtime, Java runtime,
7//! libc, etc.), at the moment only supporting Rust Standard Library PAL
8//! is on the roadmap.
9//!
10//! Note: RT.VDSO is a "fat" runtime: it creates an IO thread to interact with
11//!       sys-io, and stdio threads to provide stdin/stdout/stderr abstractions,
12//!       if needed.
13//!
14//! While it is possible to do everything RT.VDSO does by directly interacting
15//! with the OS kernel and sys-io, there are two main benefits of using a VDSO
16//! and this RT library as its proxy:
17//! - simplified integration with Rust Standard Library: instead of a "fat"
18//!   Motor OS PAL that needs heavy maintenance, this "thin" RT library
19//!   is designed to be relatively stable, even if the underlying system code
20//!   and runtime undergo extensive changes;
21//! - OS/runtime updates are automatically picked up by existing/compiled
22//!   binaries without recompilation; while this is common in Windows and Linux
23//!   with dll/so libraries, this benefit is worth mentioning here, as
24//!   Motor OS, which is based on Rust, does not support dynamic libraries,
25//!   as Rust does not support them "natively" (as in rdylib).
26#![no_std]
27#![feature(linkage)]
28
29// Mod error is the only one currently shared b/w the kernel and the userspace.
30#[macro_use]
31pub mod error;
32pub use error::*;
33
34// Constants from moto-sys: we replicate them here to avoid depending on moto-sys.
35// NOTE: do not change these numbers unless they are also changed in moto-sys!
36#[doc(hidden)]
37pub const MOTO_SYS_CUSTOM_USERSPACE_REGION_START: u64 = (1_u64 << 45) + (1_u64 << 40);
38#[doc(hidden)]
39const MOTO_SYS_CUSTOM_USERSPACE_REGION_END: u64 =
40    MOTO_SYS_CUSTOM_USERSPACE_REGION_START + (1_u64 << 40);
41#[doc(hidden)]
42const MOTO_SYS_PAGE_SIZE_SMALL: u64 = 4096;
43
44// At this address rt.vdso object will be mapped/loaded into every process/binary.
45#[doc(hidden)]
46pub const RT_VDSO_START: u64 = MOTO_SYS_CUSTOM_USERSPACE_REGION_END - (1_u64 << 32); // 4GB for RT_VDSO.
47
48// At this address rt.vdso bytes will be mapped/loaded into every process/binary.
49// NOTE: this is a temporary arrangement; when process start is moved to sys-io (or another binary),
50//       having the bytes in every process will no longer be needed.
51#[doc(hidden)]
52pub const RT_VDSO_BYTES_ADDR: u64 = RT_VDSO_START - (1_u64 << 32); // 4GB for RT_VDSO.
53
54// At this address the loader will initialize RtVdsoVtable.
55#[doc(hidden)]
56pub const RT_VDSO_VTABLE_VADDR: u64 = RT_VDSO_START - MOTO_SYS_PAGE_SIZE_SMALL;
57
58// Rust's dependency on libc runs deep, without these many binaries
59// fail to link.
60#[cfg(any(feature = "libc", feature = "rustc-dep-of-std"))]
61pub mod libc;
62
63#[cfg(not(feature = "base"))]
64pub mod alloc;
65#[cfg(not(feature = "base"))]
66pub mod fs;
67#[cfg(not(feature = "base"))]
68pub mod futex;
69#[cfg(not(feature = "base"))]
70pub mod mutex;
71#[cfg(not(feature = "base"))]
72pub mod net;
73
74#[cfg(not(feature = "base"))]
75#[allow(nonstandard_style)]
76pub mod netc;
77
78#[cfg(not(feature = "base"))]
79pub mod poll;
80#[cfg(not(feature = "base"))]
81pub mod process;
82#[cfg(not(feature = "base"))]
83pub mod thread;
84#[cfg(not(feature = "base"))]
85pub mod time;
86#[cfg(not(feature = "base"))]
87pub mod tls;
88
89pub mod spinlock;
90
91#[cfg(not(feature = "base"))]
92pub use futex::*;
93
94#[cfg(not(feature = "base"))]
95use core::sync::atomic::{AtomicU64, Ordering};
96
97/// Runtime FD (file descriptor). While Motor OS uses SysHandle
98/// for file objects internally, Rust defines std::os::fd::RawFd
99/// as c_int, so we have to follow suit to make our lives easier.
100#[cfg(not(feature = "base"))]
101pub type RtFd = i32;
102// Use posix constants: stdio is a posix construct anyway.
103#[cfg(not(feature = "base"))]
104pub const FD_STDIN: RtFd = 0;
105#[cfg(not(feature = "base"))]
106pub const FD_STDOUT: RtFd = 1;
107#[cfg(not(feature = "base"))]
108pub const FD_STDERR: RtFd = 2;
109
110#[cfg(not(feature = "base"))]
111pub const RT_VERSION: u64 = 10;
112
113/// The main VDSO vtable. Versioning happens via passing RT_VERSION
114/// constant to vdso_entry. In theory, the VDSO object can support
115/// multiple versions, so that older binaries may run on newer versions
116/// of Motor OS. In practice this flexibility is postponed until
117/// later, probably until Motor OS becomes an officially supported
118/// Rust target.
119#[cfg(not(feature = "base"))]
120#[doc(hidden)]
121#[repr(C)]
122pub struct RtVdsoVtable {
123    // This function is called to initialize this VTable
124    // (i.e. all fields other than vdso_entry and vdso_bytes_sz).
125    // Initialized by the loader/parent.
126    pub vdso_entry: AtomicU64,
127
128    // The size of vdso bytes (the address is fixed at RT_VDSO_BYTES_ADDR).
129    // Initialized by the loader/parent.
130    pub vdso_bytes_sz: AtomicU64,
131
132    // Some utilities.
133    pub log_to_kernel: AtomicU64,
134    pub log_backtrace: AtomicU64,
135    pub fill_random_bytes: AtomicU64,
136    pub num_cpus: AtomicU64,
137    pub internal_helper: AtomicU64,
138
139    // Memory management.
140    pub alloc: AtomicU64,
141    pub alloc_zeroed: AtomicU64,
142    pub realloc: AtomicU64,
143    pub dealloc: AtomicU64,
144    pub release_handle: AtomicU64,
145
146    // Time management.
147    pub time_instant_now: AtomicU64,
148    pub time_ticks_to_nanos: AtomicU64,
149    pub time_nanos_to_ticks: AtomicU64,
150    pub time_ticks_in_sec: AtomicU64,
151    pub time_abs_ticks_to_nanos: AtomicU64,
152
153    // Futex.
154    pub futex_wait: AtomicU64,
155    pub futex_wake: AtomicU64,
156    pub futex_wake_all: AtomicU64,
157
158    // Process-related.
159    pub proc_args: AtomicU64,
160    pub proc_get_full_env: AtomicU64,
161    pub proc_getenv: AtomicU64,
162    pub proc_setenv: AtomicU64,
163    pub proc_spawn: AtomicU64,
164    pub proc_kill: AtomicU64,
165    pub proc_wait: AtomicU64,
166    pub proc_status: AtomicU64,
167    pub proc_exit: AtomicU64,
168
169    // Thread Local Storage.
170    pub tls_create: AtomicU64,
171    pub tls_set: AtomicU64,
172    pub tls_get: AtomicU64,
173    pub tls_destroy: AtomicU64,
174
175    // Thread management.
176    pub thread_spawn: AtomicU64,
177    pub thread_sleep: AtomicU64,
178    pub thread_yield: AtomicU64,
179    pub thread_set_name: AtomicU64,
180    pub thread_join: AtomicU64,
181
182    // Filesystem.
183    pub fs_is_terminal: AtomicU64,
184    pub fs_open: AtomicU64,
185    pub fs_close: AtomicU64,
186    pub fs_get_file_attr: AtomicU64,
187    pub fs_fsync: AtomicU64,
188    pub fs_datasync: AtomicU64,
189    pub fs_truncate: AtomicU64,
190    pub fs_read: AtomicU64,
191    pub fs_read_vectored: AtomicU64,
192    pub fs_write: AtomicU64,
193    pub fs_write_vectored: AtomicU64,
194    pub fs_flush: AtomicU64,
195    pub fs_seek: AtomicU64,
196    pub fs_mkdir: AtomicU64,
197    pub fs_unlink: AtomicU64,
198    pub fs_rename: AtomicU64,
199    pub fs_rmdir: AtomicU64,
200    pub fs_rmdir_all: AtomicU64,
201    pub fs_set_perm: AtomicU64,
202    pub fs_set_file_perm: AtomicU64,
203    pub fs_stat: AtomicU64,
204    pub fs_canonicalize: AtomicU64,
205    pub fs_copy: AtomicU64,
206    pub fs_opendir: AtomicU64,
207    pub fs_closedir: AtomicU64,
208    pub fs_readdir: AtomicU64,
209    pub fs_getcwd: AtomicU64,
210    pub fs_chdir: AtomicU64,
211    pub fs_duplicate: AtomicU64,
212
213    // Networking.
214    pub dns_lookup: AtomicU64,
215    pub net_bind: AtomicU64,
216    pub net_listen: AtomicU64,
217    pub net_accept: AtomicU64,
218    pub net_tcp_connect: AtomicU64,
219    pub net_udp_connect: AtomicU64,
220    pub net_socket_addr: AtomicU64,
221    pub net_peer_addr: AtomicU64,
222    pub net_setsockopt: AtomicU64,
223    pub net_getsockopt: AtomicU64,
224    pub net_peek: AtomicU64,
225    pub net_udp_recv_from: AtomicU64,
226    pub net_udp_peek_from: AtomicU64,
227    pub net_udp_send_to: AtomicU64,
228
229    // Polling.
230    pub poll_new: AtomicU64,
231    pub poll_add: AtomicU64,
232    pub poll_set: AtomicU64,
233    pub poll_del: AtomicU64,
234    pub poll_wait: AtomicU64,
235    pub poll_wake: AtomicU64,
236}
237
238#[cfg(not(feature = "base"))]
239const _SIZE_CHECK: () = assert!(size_of::<RtVdsoVtable>() <= 4096);
240
241#[cfg(not(feature = "base"))]
242#[doc(hidden)]
243impl RtVdsoVtable {
244    pub fn get() -> &'static Self {
245        // Safety: sys-io is supposed to have taken care of this.
246        unsafe {
247            (RT_VDSO_VTABLE_VADDR as usize as *const RtVdsoVtable)
248                .as_ref()
249                .unwrap_unchecked()
250        }
251    }
252}
253
254#[cfg(not(feature = "base"))]
255#[doc(hidden)]
256pub fn init() {
257    assert_ne!(0, RtVdsoVtable::get().vdso_entry.load(Ordering::Acquire));
258    let vdso_entry: extern "C" fn(u64) = unsafe {
259        core::mem::transmute(
260            RtVdsoVtable::get().vdso_entry.load(Ordering::Relaxed) as usize as *const (),
261        )
262    };
263
264    vdso_entry(RT_VERSION)
265}
266
267#[cfg(not(feature = "base"))]
268#[linkage = "weak"]
269#[no_mangle]
270pub extern "C" fn moturus_runtime_start() {
271    // This function is a weak symbol because sys-io re-defines
272    // moturus_runtime_start(): sys-io is loaded by the kernel and has its
273    // own runtime initialization dance that is different from all other
274    // userspace processes.
275    init();
276}
277
278#[cfg(not(feature = "base"))]
279#[doc(hidden)]
280pub fn start() {
281    // Called by Rust stdlib in moturus_start (sys/pal/moturus/mod.rs)
282    // before main is called.
283    moturus_runtime_start();
284}
285
286#[cfg(not(feature = "base"))]
287pub fn fill_random_bytes(bytes: &mut [u8]) {
288    let vdso_fill_random_bytes: extern "C" fn(*mut u8, usize) = unsafe {
289        core::mem::transmute(
290            RtVdsoVtable::get()
291                .fill_random_bytes
292                .load(Ordering::Relaxed) as usize as *const (),
293        )
294    };
295
296    vdso_fill_random_bytes(bytes.as_mut_ptr(), bytes.len())
297}
298
299/// The number of CPUs available.
300#[cfg(not(feature = "base"))]
301pub fn num_cpus() -> usize {
302    // Although num_cpus in part KernelStaticPage in sys-io crate
303    // and theoretically available without calling into the VDSO,
304    // we want to keep moto-rt lean and mean and not depend on
305    // extra crates, so we plump num_cpus() through vdso.
306    let vdso_num_cpus: extern "C" fn() -> usize = unsafe {
307        core::mem::transmute(
308            RtVdsoVtable::get().num_cpus.load(Ordering::Relaxed) as usize as *const (),
309        )
310    };
311
312    vdso_num_cpus()
313}
314
315#[cfg(not(feature = "base"))]
316#[doc(hidden)]
317pub fn internal_helper(a0: u64, a1: u64, a2: u64, a3: u64, a4: u64, a5: u64) -> u64 {
318    // This is an internal helper used during development and testing of the VDSO.
319    // Useful as it allows an extra 'poke' into the VDSO without changing moto-rt/stdlib.
320    //
321    // May panic in a release version, so should not be used for anything other than
322    // during VDSO development.
323    let vdso_internal_helper: extern "C" fn(u64, u64, u64, u64, u64, u64) -> u64 = unsafe {
324        core::mem::transmute(
325            RtVdsoVtable::get().internal_helper.load(Ordering::Relaxed) as usize as *const (),
326        )
327    };
328
329    vdso_internal_helper(a0, a1, a2, a3, a4, a5)
330}
331
332#[cfg(not(test))]
333#[cfg(feature = "rustc-dep-of-std")]
334#[panic_handler]
335fn _panic(info: &core::panic::PanicInfo<'_>) -> ! {
336    error::log_panic(info);
337    // If the panic is logged to stderr, we should sleep a bit before exiting, as our stdio is async.
338    crate::thread::sleep_until(crate::time::Instant::now() + core::time::Duration::from_micros(100));
339    process::exit(-1)
340}