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, RuntimeThreadControl, SharedCompConfig, TlsTemplateInfo, MONITOR_INSTANCE_ID,
14};
15use secgate::util::HandleMgr;
16use space::Space;
17use thread::DEFAULT_STACK_SIZE;
18use twizzler_abi::{
19 syscall::sys_thread_exit,
20 upcall::{ResumeFlags, UpcallData, UpcallFrame},
21};
22use twizzler_rt_abi::{
23 error::{GenericError, TwzError},
24 object::{MapFlags, ObjID},
25 thread::ThreadSpawnArgs,
26};
27
28use self::{
29 compartment::{CompConfigObject, CompartmentHandle, RunComp},
30 space::{MapHandle, MapInfo, Unmapper},
31 thread::{ManagedThread, ThreadCleaner},
32};
33use crate::{gates::MonitorCompControlCmd, init::InitDynlinkContext};
34
35pub(crate) mod compartment;
36pub mod library;
37pub(crate) mod space;
38pub mod stat;
39pub(crate) mod thread;
40
41pub struct Monitor {
49 locks: LockCollection<MonitorLocks<'static>>,
50 unmapper: OnceLock<Unmapper>,
51 pub space: &'static Mutex<space::Space>,
53 pub thread_mgr: &'static RwLock<thread::ThreadMgr>,
55 pub comp_mgr: &'static RwLock<compartment::CompartmentMgr>,
57 pub dynlink: &'static RwLock<&'static mut dynlink::context::Context>,
59 pub library_handles: &'static RwLock<HandleMgr<library::LibraryHandle>>,
61 pub _compartment_handles: &'static RwLock<HandleMgr<CompartmentHandle>>,
63}
64
65type MonitorLocks<'a> = (
68 &'a RwLock<thread::ThreadMgr>,
69 &'a RwLock<compartment::CompartmentMgr>,
70 &'a RwLock<&'static mut dynlink::context::Context>,
71 &'a RwLock<HandleMgr<library::LibraryHandle>>,
72 &'a RwLock<HandleMgr<CompartmentHandle>>,
73);
74
75impl Monitor {
76 pub fn start_background_threads(&self) {
79 let cleaner = ThreadCleaner::new();
80 self.unmapper.set(Unmapper::new()).ok().unwrap();
81 self.thread_mgr
82 .write(ThreadKey::get().unwrap())
83 .set_cleaner(cleaner);
84 }
85
86 pub fn new(init: InitDynlinkContext) -> Self {
88 let mut comp_mgr = compartment::CompartmentMgr::default();
89 let space = Mutex::new(space::Space::default());
90
91 let ctx = init.get_safe_context();
92 let super_tls = ctx
94 .get_compartment_mut(MONITOR_COMPARTMENT_ID)
95 .unwrap()
96 .build_tls_region(RuntimeThreadControl::default(), |layout| unsafe {
97 NonNull::new(std::alloc::alloc_zeroed(layout))
98 })
99 .unwrap();
100 let template: &'static TlsTemplateInfo = Box::leak(Box::new(super_tls.into()));
101
102 let monitor_scc =
104 SharedCompConfig::new(MONITOR_INSTANCE_ID, template as *const _ as *mut _);
105 let cc_handle = Space::safe_create_and_map_runtime_object(
106 &space,
107 MONITOR_INSTANCE_ID,
108 MapFlags::READ | MapFlags::WRITE,
109 )
110 .unwrap();
111 let stack_handle = Space::safe_create_and_map_runtime_object(
112 &space,
113 MONITOR_INSTANCE_ID,
114 MapFlags::READ | MapFlags::WRITE,
115 )
116 .unwrap();
117 comp_mgr.insert(RunComp::new(
118 MONITOR_INSTANCE_ID,
119 MONITOR_INSTANCE_ID,
120 "monitor".to_string(),
121 MONITOR_COMPARTMENT_ID,
122 vec![],
123 CompConfigObject::new(cc_handle, monitor_scc),
124 (CompartmentFlags::READY | CompartmentFlags::STARTED).bits(),
125 StackObject::new(stack_handle, DEFAULT_STACK_SIZE).unwrap(),
126 0, &[],
129 false,
130 ));
131
132 let space = Box::leak(Box::new(space));
135 let thread_mgr = Box::leak(Box::new(RwLock::new(thread::ThreadMgr::default())));
136 let comp_mgr = Box::leak(Box::new(RwLock::new(comp_mgr)));
137 let dynlink = Box::leak(Box::new(RwLock::new(ctx)));
138 let library_handles = Box::leak(Box::new(RwLock::new(HandleMgr::new(None))));
139 let compartment_handles = Box::leak(Box::new(RwLock::new(HandleMgr::new(None))));
140
141 Self {
143 locks: LockCollection::try_new((
144 &*thread_mgr,
145 &*comp_mgr,
146 &*dynlink,
147 &*library_handles,
148 &*compartment_handles,
149 ))
150 .unwrap(),
151 unmapper: OnceLock::new(),
152 space,
153 thread_mgr,
154 comp_mgr,
155 dynlink,
156 library_handles,
157 _compartment_handles: compartment_handles,
158 }
159 }
160
161 #[tracing::instrument(skip(self, main), level = tracing::Level::DEBUG)]
163 pub fn start_thread(&self, main: Box<dyn FnOnce()>) -> Result<ManagedThread, TwzError> {
164 let key = ThreadKey::get().unwrap();
165 let locks = &mut *self.locks.lock(key);
166
167 let monitor_dynlink_comp = locks.2.get_compartment_mut(MONITOR_COMPARTMENT_ID).unwrap();
168 locks.0.start_thread(monitor_dynlink_comp, main, None)
169 }
170
171 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
173 pub fn spawn_compartment_thread(
174 &self,
175 instance: ObjID,
176 args: ThreadSpawnArgs,
177 stack_ptr: usize,
178 thread_ptr: usize,
179 ) -> Result<ObjID, TwzError> {
180 let thread = self.start_thread(Box::new(move || {
181 let frame = UpcallFrame::new_entry_frame(
182 stack_ptr,
183 args.stack_size,
184 thread_ptr,
185 instance,
186 args.start,
187 args.arg,
188 );
189 unsafe {
190 twizzler_abi::syscall::sys_thread_resume_from_upcall(&frame, ResumeFlags::empty())
191 };
192 }))?;
193 Ok(thread.id)
194 }
195
196 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
198 pub fn get_comp_config(&self, sctx: ObjID) -> Result<*const SharedCompConfig, TwzError> {
199 let comps = self.comp_mgr.write(ThreadKey::get().unwrap());
200 Ok(comps.get(sctx)?.comp_config_ptr())
201 }
202
203 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
205 pub fn map_object(&self, sctx: ObjID, info: MapInfo) -> Result<MapHandle, TwzError> {
206 let handle = Space::map(&self.space, info)?;
207
208 let mut comp_mgr = self.comp_mgr.write(ThreadKey::get().unwrap());
209 let rc = comp_mgr.get_mut(sctx)?;
210 let handle = rc.map_object(info, handle)?;
211 Ok(handle)
212 }
213
214 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
216 pub fn map_pair(
217 &self,
218 sctx: ObjID,
219 info: MapInfo,
220 info2: MapInfo,
221 ) -> Result<(MapHandle, MapHandle), TwzError> {
222 let (handle, handle2) = self.space.lock().unwrap().map_pair(info, info2)?;
223
224 let mut comp_mgr = self.comp_mgr.write(ThreadKey::get().unwrap());
225 let rc = comp_mgr.get_mut(sctx)?;
226 let handle = rc.map_object(info, handle)?;
227 let handle2 = rc.map_object(info2, handle2)?;
228 Ok((handle, handle2))
229 }
230
231 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
233 pub fn unmap_object(&self, sctx: ObjID, info: MapInfo) {
234 let Some(key) = ThreadKey::get() else {
235 tracing::warn!("todo: recursive locked unmap");
236 return;
237 };
238
239 let mut comp_mgr = self.comp_mgr.write(key);
240 if let Ok(comp) = comp_mgr.get_mut(sctx) {
241 let handle = comp.unmap_object(info);
242 drop(comp_mgr);
243 drop(handle);
244 }
245 }
246
247 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
249 pub fn get_thread_simple_buffer(&self, sctx: ObjID, thread: ObjID) -> Result<ObjID, TwzError> {
250 let mut locks = self.locks.lock(ThreadKey::get().unwrap());
251 let (_, ref mut comps, _, _, _) = *locks;
252 let rc = comps.get_mut(sctx)?;
253 let pt = rc.get_per_thread(thread);
254 pt.simple_buffer_id().ok_or(GenericError::Internal.into())
255 }
256
257 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
259 pub fn _write_thread_simple_buffer(
260 &self,
261 sctx: ObjID,
262 thread: ObjID,
263 bytes: &[u8],
264 ) -> Result<usize, TwzError> {
265 let mut locks = self.locks.lock(ThreadKey::get().unwrap());
266 let (_, ref mut comps, _, _, _) = *locks;
267 let rc = comps.get_mut(sctx)?;
268 let pt = rc.get_per_thread(thread);
269 Ok(pt.write_bytes(bytes))
270 }
271
272 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
274 pub fn read_thread_simple_buffer(
275 &self,
276 sctx: ObjID,
277 thread: ObjID,
278 len: usize,
279 ) -> Result<Vec<u8>, TwzError> {
280 let t = ThreadKey::get().unwrap();
281 let mut locks = self.locks.lock(t);
282 let (_, ref mut comps, _, _, _) = *locks;
283 let rc = comps.get_mut(sctx)?;
284 let pt = rc.get_per_thread(thread);
285 Ok(pt.read_bytes(len))
286 }
287
288 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
290 pub fn comp_name(&self, id: ObjID) -> Result<String, TwzError> {
291 self.comp_mgr
292 .read(ThreadKey::get().unwrap())
293 .get(id)
294 .map(|rc| rc.name.clone())
295 }
296
297 pub fn upcall_handle(
298 &self,
299 frame: &mut UpcallFrame,
300 info: &UpcallData,
301 ) -> Result<Option<ResumeFlags>, TwzError> {
302 self.comp_mgr
303 .write(ThreadKey::get().unwrap())
304 .get_mut(frame.prior_ctx)?
305 .upcall_handle(frame, info)
306 }
307
308 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
310 pub fn compartment_ctrl(
311 &self,
312 info: &secgate::GateCallInfo,
313 cmd: MonitorCompControlCmd,
314 ) -> Option<i32> {
315 let src = info.source_context()?;
316 tracing::debug!(
317 "compartment ctrl from: {:?}, thread = {:?}: {:?}",
318 src,
319 info.thread_id(),
320 cmd
321 );
322 match cmd {
323 MonitorCompControlCmd::RuntimeReady => loop {
331 let state = self.load_compartment_flags(src);
332 if state & COMP_STARTED == 0
333 || state & COMP_DESTRUCTED != 0
334 || state & COMP_EXITED != 0
335 {
336 tracing::warn!(
337 "runtime main thread {} encountered invalid compartment {} state: {}",
338 info.thread_id(),
339 src,
340 state
341 );
342 sys_thread_exit(127);
343 }
344
345 if self.update_compartment_flags(src, |state| Some(state | COMP_READY)) {
346 tracing::debug!(
347 "runtime main thread reached compartment ready state in {}: {:x}",
348 self.comp_name(src)
349 .unwrap_or_else(|_| String::from("unknown")),
350 state
351 );
352 break if state & COMP_IS_BINARY == 0 {
353 Some(0)
354 } else {
355 None
356 };
357 }
358 },
359 MonitorCompControlCmd::RuntimePostMain => {
360 loop {
363 if self.update_compartment_flags(src, |state| {
364 if state & COMP_IS_BINARY != 0 {
366 Some(state | COMP_THREAD_CAN_EXIT)
367 } else {
368 Some(state)
369 }
370 }) {
371 tracing::debug!(
372 "runtime main thread reached compartment post-main state in {}",
373 self.comp_name(src)
374 .unwrap_or_else(|_| String::from("unknown"))
375 );
376 break;
377 }
378 }
379 loop {
382 let flags = self.load_compartment_flags(src);
383 if flags & COMP_THREAD_CAN_EXIT != 0
384 && self.update_compartment_flags(src, |state| Some(state | COMP_DESTRUCTED))
385 {
386 tracing::debug!(
387 "runtime main thread destructing in {}",
388 self.comp_name(src)
389 .unwrap_or_else(|_| String::from("unknown"))
390 );
391 break None;
392 }
393 self.wait_for_compartment_state_change(src, flags);
394 }
395 }
396 }
397 }
398
399 #[tracing::instrument(skip(self), level = tracing::Level::DEBUG)]
400 pub fn set_nameroot(&self, _info: &secgate::GateCallInfo, root: ObjID) -> Result<(), TwzError> {
401 crate::dlengine::set_naming(root)
402 }
403}
404
405static MONITOR: OnceLock<Monitor> = OnceLock::new();
406
407pub fn get_monitor() -> &'static Monitor {
409 MONITOR.get().unwrap()
410}
411
412pub fn set_monitor(monitor: Monitor) {
415 if MONITOR.set(monitor).is_err() {
416 panic!("second call to set_monitor");
417 }
418}
419
420pub use space::early_object_map;