1use std::{
2 ptr::NonNull,
3 sync::{Mutex, OnceLock},
4};
5
6use compartment::{
7 StackObject, COMP_DESTRUCTED, COMP_EXITED, COMP_IS_BINARY, COMP_READY, COMP_STARTED,
8 COMP_THREAD_CAN_EXIT,
9};
10use dynlink::compartment::MONITOR_COMPARTMENT_ID;
11use happylock::{LockCollection, RwLock, ThreadKey};
12use monitor_api::{
13 CompartmentFlags, MonitorCompControlCmd, PostSignalFlags, RuntimeThreadControl,
14 SharedCompConfig, TlsTemplateInfo, MONITOR_INSTANCE_ID,
15};
16use secgate::util::HandleMgr;
17use space::Space;
18use talc::{ErrOnOom, Talc};
19use thread::DEFAULT_STACK_SIZE;
20use twizzler_abi::{
21 syscall::{sys_thread_exit, sys_thread_send_message},
22 upcall::{ResumeFlags, UpcallData, UpcallFrame},
23};
24use twizzler_rt_abi::{
25 error::{GenericError, TwzError},
26 object::{MapFlags, ObjID},
27 thread::ThreadSpawnArgs,
28};
29
30use self::{
31 compartment::{CompConfigObject, CompartmentHandle, RunComp},
32 space::{MapHandle, MapInfo, Unmapper},
33 thread::{ManagedThread, ThreadCleaner},
34};
35use crate::init::InitDynlinkContext;
36
37pub(crate) mod compartment;
38pub mod library;
39pub(crate) mod space;
40pub mod stat;
41pub(crate) mod thread;
42
43pub struct Monitor {
51 locks: LockCollection<MonitorLocks<'static>>,
52 unmapper: OnceLock<Unmapper>,
53 pub space: &'static Mutex<space::Space>,
55 pub thread_mgr: &'static RwLock<thread::ThreadMgr>,
57 pub comp_mgr: &'static RwLock<compartment::CompartmentMgr>,
59 pub dynlink: &'static RwLock<&'static mut dynlink::context::Context>,
61 pub library_handles: &'static RwLock<HandleMgr<library::LibraryHandle>>,
63 pub _compartment_handles: &'static RwLock<HandleMgr<CompartmentHandle>>,
65}
66
67type MonitorLocks<'a> = (
70 &'a RwLock<thread::ThreadMgr>,
71 &'a RwLock<compartment::CompartmentMgr>,
72 &'a RwLock<&'static mut dynlink::context::Context>,
73 &'a RwLock<HandleMgr<library::LibraryHandle>>,
74 &'a RwLock<HandleMgr<CompartmentHandle>>,
75);
76
77impl Monitor {
78 pub fn start_background_threads(&self) {
81 let cleaner = ThreadCleaner::new();
82 self.unmapper.set(Unmapper::new()).ok().unwrap();
83 self.thread_mgr
84 .write(ThreadKey::get().unwrap())
85 .set_cleaner(cleaner);
86 }
87
88 pub fn new(init: InitDynlinkContext) -> Self {
90 let mut comp_mgr = compartment::CompartmentMgr::default();
91 let space = Mutex::new(space::Space::default());
92
93 let ctx = init.get_safe_context();
94 let super_tls = ctx
96 .get_compartment_mut(MONITOR_COMPARTMENT_ID)
97 .unwrap()
98 .build_tls_region(RuntimeThreadControl::default(), |layout| unsafe {
99 NonNull::new(std::alloc::alloc_zeroed(layout))
100 })
101 .unwrap();
102 let template: &'static TlsTemplateInfo = Box::leak(Box::new(super_tls.into()));
103
104 let monitor_scc = SharedCompConfig::new(
106 MONITOR_INSTANCE_ID,
107 template as *const _ as *mut _,
108 monitor_api::CompartmentLoaderConfig::default(),
109 );
110 let cc_handle = Space::safe_create_and_map_runtime_object(
111 &space,
112 MONITOR_INSTANCE_ID,
113 MapFlags::READ | MapFlags::WRITE,
114 )
115 .unwrap();
116 let stack_handle = Space::safe_create_and_map_runtime_object(
117 &space,
118 MONITOR_INSTANCE_ID,
119 MapFlags::READ | MapFlags::WRITE,
120 )
121 .unwrap();
122
123 let comp_config = CompConfigObject::new(cc_handle, monitor_scc);
124 let mut alloc = Talc::new(ErrOnOom);
125 unsafe { alloc.claim(comp_config.alloc_span()).unwrap() };
126 comp_mgr.insert(RunComp::new(
127 MONITOR_INSTANCE_ID,
128 MONITOR_INSTANCE_ID,
129 "monitor".to_string(),
130 MONITOR_COMPARTMENT_ID,
131 vec![],
132 comp_config,
133 (CompartmentFlags::READY | CompartmentFlags::STARTED).bits(),
134 StackObject::new(stack_handle, DEFAULT_STACK_SIZE).unwrap(),
135 0, 0,
138 &[],
139 false,
140 None,
141 alloc,
142 ));
143
144 let space = Box::leak(Box::new(space));
147 let thread_mgr = Box::leak(Box::new(RwLock::new(thread::ThreadMgr::default())));
148 let comp_mgr = Box::leak(Box::new(RwLock::new(comp_mgr)));
149 let dynlink = Box::leak(Box::new(RwLock::new(ctx)));
150 let library_handles = Box::leak(Box::new(RwLock::new(HandleMgr::new(None))));
151 let compartment_handles = Box::leak(Box::new(RwLock::new(HandleMgr::new(None))));
152
153 Self {
155 locks: LockCollection::try_new((
156 &*thread_mgr,
157 &*comp_mgr,
158 &*dynlink,
159 &*library_handles,
160 &*compartment_handles,
161 ))
162 .unwrap(),
163 unmapper: OnceLock::new(),
164 space,
165 thread_mgr,
166 comp_mgr,
167 dynlink,
168 library_handles,
169 _compartment_handles: compartment_handles,
170 }
171 }
172
173 #[tracing::instrument(skip(self, main), level = tracing::Level::DEBUG)]
175 pub fn start_thread(
176 &self,
177 instance: ObjID,
178 main: Box<dyn FnOnce()>,
179 ) -> Result<ManagedThread, TwzError> {
180 let key = ThreadKey::get().unwrap();
181 let locks = &mut *self.locks.lock(key);
182
183 let monitor_dynlink_comp = locks.2.get_compartment_mut(MONITOR_COMPARTMENT_ID).unwrap();
184 locks
185 .0
186 .start_thread(monitor_dynlink_comp, main, None, instance)
187 }
188
189 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
191 pub fn spawn_compartment_thread(
192 &self,
193 instance: ObjID,
194 args: ThreadSpawnArgs,
195 stack_ptr: usize,
196 thread_ptr: usize,
197 ) -> Result<ObjID, TwzError> {
198 let thread = self.start_thread(
199 instance,
200 Box::new(move || {
201 if instance.raw() != 0 {
202 let _ = twizzler_abi::syscall::sys_sctx_attach(instance);
203 }
204 let frame = UpcallFrame::new_entry_frame(
205 stack_ptr,
206 args.stack_size,
207 thread_ptr,
208 instance,
209 args.start,
210 args.arg,
211 );
212 unsafe {
213 twizzler_abi::syscall::sys_thread_resume_from_upcall(
214 &frame,
215 ResumeFlags::empty(),
216 )
217 };
218 }),
219 )?;
220 let mon = get_monitor();
221 let mut comps = mon.comp_mgr.write(ThreadKey::get().unwrap());
222 let _pt = comps.get_mut(instance)?.get_per_thread(thread.id);
224 Ok(thread.id)
225 }
226
227 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
229 pub fn get_comp_config(&self, sctx: ObjID) -> Result<*const SharedCompConfig, TwzError> {
230 let comps = self.comp_mgr.write(ThreadKey::get().unwrap());
231 Ok(comps.get(sctx)?.comp_config_ptr())
232 }
233
234 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
236 pub fn map_object(&self, sctx: ObjID, info: MapInfo) -> Result<MapHandle, TwzError> {
237 let handle = Space::map(&self.space, info)?;
238
239 let mut comp_mgr = self.comp_mgr.write(ThreadKey::get().unwrap());
240 let rc = comp_mgr.get_mut(sctx)?;
241 let handle = rc.map_object(info, handle)?;
242 Ok(handle)
243 }
244
245 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
247 pub fn map_pair(
248 &self,
249 sctx: ObjID,
250 info: MapInfo,
251 info2: MapInfo,
252 ) -> Result<(MapHandle, MapHandle), TwzError> {
253 let (handle, handle2) = self.space.lock().unwrap().map_pair(info, info2)?;
254
255 let mut comp_mgr = self.comp_mgr.write(ThreadKey::get().unwrap());
256 let rc = comp_mgr.get_mut(sctx)?;
257 let handle = rc.map_object(info, handle)?;
258 let handle2 = rc.map_object(info2, handle2)?;
259 Ok((handle, handle2))
260 }
261
262 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
264 pub fn unmap_object(&self, sctx: ObjID, info: MapInfo) {
265 let Some(key) = ThreadKey::get() else {
266 tracing::warn!("todo: recursive locked unmap");
267 return;
268 };
269
270 let mut comp_mgr = self.comp_mgr.write(key);
271 if let Ok(comp) = comp_mgr.get_mut(sctx) {
272 let handle = comp.unmap_object(info);
273 drop(comp_mgr);
274 drop(handle);
275 }
276 }
277
278 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
280 pub fn get_thread_simple_buffer(&self, sctx: ObjID, thread: ObjID) -> Result<ObjID, TwzError> {
281 let mut locks = self.locks.lock(ThreadKey::get().unwrap());
282 let (_, ref mut comps, _, _, _) = *locks;
283 let rc = comps.get_mut(sctx)?;
284 let pt = rc.get_per_thread(thread);
285 pt.simple_buffer_id().ok_or(GenericError::Internal.into())
286 }
287
288 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
290 pub fn _write_thread_simple_buffer(
291 &self,
292 sctx: ObjID,
293 thread: ObjID,
294 bytes: &[u8],
295 ) -> Result<usize, TwzError> {
296 let mut locks = self.locks.lock(ThreadKey::get().unwrap());
297 let (_, ref mut comps, _, _, _) = *locks;
298 let rc = comps.get_mut(sctx)?;
299 let pt = rc.get_per_thread(thread);
300 Ok(pt.write_bytes(bytes))
301 }
302
303 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
305 pub fn read_thread_simple_buffer(
306 &self,
307 sctx: ObjID,
308 thread: ObjID,
309 len: usize,
310 ) -> Result<Vec<u8>, TwzError> {
311 let t = ThreadKey::get().unwrap();
312 let mut locks = self.locks.lock(t);
313 let (_, ref mut comps, _, _, _) = *locks;
314 let rc = comps.get_mut(sctx)?;
315 let pt = rc.get_per_thread(thread);
316 Ok(pt.read_bytes(len))
317 }
318
319 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
321 pub fn comp_name(&self, id: ObjID) -> Result<String, TwzError> {
322 self.comp_mgr
323 .read(ThreadKey::get().unwrap())
324 .get(id)
325 .map(|rc| rc.name.clone())
326 }
327
328 pub fn upcall_handle(
329 &self,
330 frame: &mut UpcallFrame,
331 info: &UpcallData,
332 ) -> Result<Option<ResumeFlags>, TwzError> {
333 self.comp_mgr
334 .write(ThreadKey::get().unwrap())
335 .get_mut(frame.prior_ctx)?
336 .upcall_handle(frame, info)
337 }
338
339 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
341 pub fn compartment_ctrl(
342 &self,
343 info: &secgate::GateCallInfo,
344 cmd: MonitorCompControlCmd,
345 ) -> Option<i32> {
346 let src = info.source_context()?;
347 tracing::debug!(
348 "compartment ctrl from: {:?}, thread = {:?}: {:?}",
349 src,
350 info.thread_id(),
351 cmd
352 );
353 match cmd {
354 MonitorCompControlCmd::RuntimeReady => loop {
362 let state = self.load_compartment_flags(src);
363 if state & COMP_STARTED == 0
364 || state & COMP_DESTRUCTED != 0
365 || state & COMP_EXITED != 0
366 {
367 tracing::warn!(
368 "runtime main thread {} encountered invalid compartment {} state: {}",
369 info.thread_id(),
370 src,
371 state
372 );
373 sys_thread_exit(127);
374 }
375
376 if self.update_compartment_flags(src, |state| Some(state | COMP_READY)) {
377 tracing::debug!(
378 "runtime main thread reached compartment ready state in {}: {:x}",
379 self.comp_name(src)
380 .unwrap_or_else(|_| String::from("unknown")),
381 state
382 );
383 break if state & COMP_IS_BINARY == 0 {
384 Some(0)
385 } else {
386 None
387 };
388 }
389 },
390 MonitorCompControlCmd::RuntimePostMain => {
391 loop {
394 if self.update_compartment_flags(src, |state| {
395 if state & COMP_IS_BINARY != 0 {
397 Some(state | COMP_THREAD_CAN_EXIT)
398 } else {
399 Some(state)
400 }
401 }) {
402 tracing::debug!(
403 "runtime main thread reached compartment post-main state in {}",
404 self.comp_name(src)
405 .unwrap_or_else(|_| String::from("unknown"))
406 );
407 break;
408 }
409 }
410 loop {
413 let flags = self.load_compartment_flags(src);
414 if flags & COMP_THREAD_CAN_EXIT != 0
415 && self.update_compartment_flags(src, |state| Some(state | COMP_DESTRUCTED))
416 {
417 tracing::debug!(
418 "runtime main thread destructing in {}",
419 self.comp_name(src)
420 .unwrap_or_else(|_| String::from("unknown"))
421 );
422 break None;
423 }
424 self.wait_for_compartment_state_change(src, flags);
425 }
426 }
427 }
428 }
429
430 pub fn post_signal(
431 &self,
432 info: &secgate::GateCallInfo,
433 target: Option<ObjID>,
434 signal: u64,
435 flags: PostSignalFlags,
436 ) -> Result<(), TwzError> {
437 let target = target.unwrap_or(info.source_context().unwrap_or(MONITOR_INSTANCE_ID));
438 let post_signal = |target: ObjID, sig: u64| -> Result<(), TwzError> {
439 tracing::debug!("posting signal {} to {}", sig, target);
440 let comp = self.comp_mgr.read(ThreadKey::get().unwrap());
441 let comp = comp.get(target)?;
442 let scc = comp.comp_config_ptr();
443 let scc = unsafe { &*scc };
444 scc.post_signal(signal);
445 if let Some(thread) = comp.main_thread() {
446 sys_thread_send_message(thread.thread.id, signal, 0)?;
447 }
448 Ok(())
449 };
450 if flags.contains(PostSignalFlags::GROUP) {
451 return Err(TwzError::NOT_SUPPORTED);
452 }
453 if flags.contains(PostSignalFlags::CONTROLLER) {
454 let targets = self
455 .comp_mgr
456 .read(ThreadKey::get().unwrap())
457 .find_controller_targets(target);
458 for t in targets {
459 let _ = post_signal(t, signal).inspect_err(|e| {
460 tracing::warn!(
461 "failed to raise signal via controller {} to target {}: {}",
462 target,
463 t,
464 e
465 )
466 });
467 }
468 } else {
469 post_signal(target, signal)?;
470 }
471 return Ok(());
472 }
473
474 pub fn set_controller(
475 &self,
476 _info: &secgate::GateCallInfo,
477 target: ObjID,
478 controller: ObjID,
479 ) -> Result<(), TwzError> {
480 let mut cm = self.comp_mgr.write(ThreadKey::get().unwrap());
481 cm.set_controller(target, controller)?;
482 return Ok(());
483 }
484
485 pub fn libname_map(
486 &self,
487 caller: ObjID,
488 thread: ObjID,
489 namelen: usize,
490 id: ObjID,
491 ) -> Result<(), TwzError> {
492 let str_bytes = self.read_thread_simple_buffer(caller, thread, namelen)?;
493
494 let name = str::from_utf8(&str_bytes).map_err(|_| TwzError::INVALID_ARGUMENT)?;
495 tracing::trace!("libname map: {}", name);
496 let mut dynlink = self.dynlink.write(ThreadKey::get().unwrap());
497 dynlink.engine.add_name_map(name, id);
498
499 Ok(())
500 }
501
502 pub fn libname_unmap(
503 &self,
504 caller: ObjID,
505 thread: ObjID,
506 namelen: Option<usize>,
507 id: Option<ObjID>,
508 ) -> Result<(), TwzError> {
509 let str_bytes = self.read_thread_simple_buffer(caller, thread, namelen.unwrap_or(0))?;
510 let name = namelen
511 .map(|_| str::from_utf8(&str_bytes).map_err(|_| TwzError::INVALID_ARGUMENT))
512 .transpose()?;
513 let mut dynlink = self.dynlink.write(ThreadKey::get().unwrap());
514 dynlink.engine.remove_name_map(name, id);
515
516 Ok(())
517 }
518}
519
520static MONITOR: OnceLock<Monitor> = OnceLock::new();
521
522pub fn get_monitor() -> &'static Monitor {
524 MONITOR.get().unwrap()
525}
526
527pub fn set_monitor(monitor: Monitor) {
530 if MONITOR.set(monitor).is_err() {
531 panic!("second call to set_monitor");
532 }
533}
534
535pub use space::early_object_map;