1use std::{
2 alloc::Layout,
3 collections::HashMap,
4 ffi::{CStr, CString},
5 ptr::{addr_of, NonNull},
6 sync::atomic::{AtomicU64, Ordering},
7};
8
9use dynlink::{compartment::CompartmentId, context::Context};
10use monitor_api::{
11 CompartmentFlags, RuntimeThreadControl, SharedCompConfig, ThreadInfo, TlsTemplateInfo,
12};
13use secgate::util::SimpleBuffer;
14use talc::{ErrOnOom, Talc};
15use twizzler_abi::{
16 syscall::{
17 DeleteFlags, ObjectControlCmd, ThreadSync, ThreadSyncFlags, ThreadSyncOp,
18 ThreadSyncReference, ThreadSyncSleep, ThreadSyncWake,
19 },
20 upcall::{ResumeFlags, UpcallData, UpcallFrame},
21};
22use twizzler_rt_abi::{
23 core::{CompartmentInitInfo, CtorSet, InitInfoPtrs, RuntimeInfo, RUNTIME_INIT_COMP},
24 error::TwzError,
25 object::{MapFlags, ObjID},
26};
27
28use super::{compconfig::CompConfigObject, compthread::CompThread, StackObject};
29use crate::mon::{
30 get_monitor,
31 space::{MapHandle, MapInfo, Space},
32 thread::ThreadMgr,
33};
34
35pub const COMP_READY: u64 = CompartmentFlags::READY.bits();
37pub const COMP_IS_BINARY: u64 = CompartmentFlags::IS_BINARY.bits();
39pub const COMP_THREAD_CAN_EXIT: u64 = CompartmentFlags::THREAD_CAN_EXIT.bits();
41pub const COMP_STARTED: u64 = CompartmentFlags::STARTED.bits();
43pub const COMP_DESTRUCTED: u64 = CompartmentFlags::DESTRUCTED.bits();
45pub const COMP_EXITED: u64 = CompartmentFlags::EXITED.bits();
47
48pub struct RunComp {
50 pub sctx: ObjID,
52 pub instance: ObjID,
54 pub name: String,
56 pub compartment_id: CompartmentId,
58 main: Option<CompThread>,
59 pub deps: Vec<ObjID>,
60 comp_config_object: CompConfigObject,
61 alloc: Talc<ErrOnOom>,
62 mapped_objects: HashMap<MapInfo, MapHandle>,
63 flags: Box<AtomicU64>,
64 pub per_thread: HashMap<ObjID, PerThread>,
65 init_info: Option<(StackObject, usize, usize, Vec<CtorSet>)>,
66 is_debugging: bool,
67 pub(crate) use_count: u64,
68 pub controller: Option<ObjID>,
69}
70
71impl Drop for RunComp {
72 fn drop(&mut self) {
73 let _ = twizzler_abi::syscall::sys_object_ctrl(
75 self.instance,
76 ObjectControlCmd::Delete(DeleteFlags::empty()),
77 )
78 .inspect_err(|e| tracing::warn!("failed to delete instance on RunComp drop: {}", e));
79 }
80}
81
82impl core::fmt::Debug for RunComp {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 f.debug_struct("RunComp")
85 .field("sctx", &self.sctx)
86 .field("instance", &self.instance)
87 .field("name", &self.name)
88 .field("deps", &self.deps)
89 .field("usecount", &self.use_count)
90 .field("dynlink_id", &self.compartment_id)
91 .finish_non_exhaustive()
92 }
93}
94
95pub struct PerThread {
97 simple_buffer: Option<(SimpleBuffer, MapHandle)>,
98}
99
100impl PerThread {
101 fn new(instance: ObjID, _th: ObjID) -> Self {
106 let handle = Space::safe_create_and_map_runtime_object(
107 &get_monitor().space,
108 instance,
109 MapFlags::READ | MapFlags::WRITE,
110 )
111 .ok();
112
113 Self {
114 simple_buffer: handle
115 .map(|handle| (SimpleBuffer::new(unsafe { handle.object_handle() }), handle)),
116 }
117 }
118
119 pub fn write_bytes(&mut self, bytes: &[u8]) -> usize {
121 self.simple_buffer
122 .as_mut()
123 .map(|sb| sb.0.write(bytes))
124 .unwrap_or(0)
125 }
126
127 pub fn read_bytes(&mut self, len: usize) -> Vec<u8> {
129 let mut v = vec![0; len];
130 let readlen = self
131 .simple_buffer
132 .as_mut()
133 .map(|sb| sb.0.read(&mut v))
134 .unwrap_or(0);
135 v.truncate(readlen);
136 v
137 }
138
139 pub fn simple_buffer_id(&self) -> Option<ObjID> {
141 Some(self.simple_buffer.as_ref()?.0.handle().id())
142 }
143}
144
145impl RunComp {
146 #[allow(clippy::too_many_arguments)]
148 pub fn new(
149 sctx: ObjID,
150 instance: ObjID,
151 name: String,
152 compartment_id: CompartmentId,
153 deps: Vec<ObjID>,
154 comp_config_object: CompConfigObject,
155 flags: u64,
156 main_stack: StackObject,
157 entry: usize,
158 main_entry: usize,
159 ctors: &[CtorSet],
160 is_debugging: bool,
161 controller: Option<ObjID>,
162 alloc: Talc<ErrOnOom>,
163 ) -> Self {
164 Self {
165 sctx,
166 is_debugging,
167 instance,
168 name,
169 compartment_id,
170 main: None,
171 deps,
172 comp_config_object,
173 alloc,
174 mapped_objects: HashMap::default(),
175 flags: Box::new(AtomicU64::new(flags)),
176 per_thread: HashMap::new(),
177 init_info: Some((main_stack, entry, main_entry, ctors.to_vec())),
178 use_count: 0,
179 controller,
180 }
181 }
182
183 pub fn get_per_thread(&mut self, id: ObjID) -> &mut PerThread {
185 self.per_thread
186 .entry(id)
187 .or_insert_with(|| PerThread::new(self.instance, id))
188 }
189
190 pub fn clean_per_thread_data(&mut self, id: ObjID) {
192 self.per_thread.remove(&id);
193 }
194
195 pub fn map_object(&mut self, info: MapInfo, handle: MapHandle) -> Result<MapHandle, TwzError> {
197 self.mapped_objects.insert(info, handle.clone());
198 Ok(handle)
199 }
200
201 pub fn unmap_object(&mut self, info: MapInfo) -> Option<MapHandle> {
203 let x = self.mapped_objects.remove(&info);
204 if x.is_none() {
205 tracing::debug!(
207 "tried to comp-unmap an object that was not mapped by compartment ({}): {:?}",
208 self.name,
209 info
210 );
211 }
212 x
213 }
214
215 pub fn comp_config_ptr(&self) -> *const SharedCompConfig {
217 self.comp_config_object.get_comp_config()
218 }
219
220 pub fn monitor_new<T: Copy + Sized>(&mut self, data: T) -> Result<*mut T, ()> {
222 unsafe {
223 let place: NonNull<T> = self.alloc.malloc(Layout::new::<T>())?.cast();
224 place.as_ptr().write(data);
225 Ok(place.as_ptr())
226 }
227 }
228
229 pub fn monitor_new_slice<T: Copy + Sized>(&mut self, data: &[T]) -> Result<*mut T, ()> {
231 unsafe {
232 let place = self.alloc.malloc(Layout::array::<T>(data.len()).unwrap())?;
233 let slice = core::slice::from_raw_parts_mut(place.as_ptr() as *mut T, data.len());
234 slice.copy_from_slice(data);
235 Ok(place.as_ptr() as *mut T)
236 }
237 }
238
239 pub fn set_flag(&self, val: u64) {
241 tracing::trace!("compartment {} set flag {:x}", self.name, val);
242 self.flags.fetch_or(val, Ordering::SeqCst);
243 self.notify_state_changed();
244 }
245
246 pub fn cas_flag(&self, old: u64, new: u64) -> Result<u64, u64> {
248 let r = self
249 .flags
250 .compare_exchange(old, new, Ordering::SeqCst, Ordering::SeqCst);
251 if r.is_ok() {
252 tracing::trace!("compartment {} cas flag {:x} -> {:x}", self.name, old, new);
253 self.notify_state_changed();
254 }
255 r
256 }
257
258 pub fn notify_state_changed(&self) {
259 let _ = twizzler_abi::syscall::sys_thread_sync(
260 &mut [ThreadSync::new_wake(ThreadSyncWake::new(
261 ThreadSyncReference::Virtual(&*self.flags),
262 usize::MAX,
263 ))],
264 None,
265 );
266 }
267
268 pub fn has_flag(&self, flag: u64) -> bool {
270 self.flags.load(Ordering::SeqCst) & flag != 0
271 }
272
273 pub fn until_change(&self, cur: u64) -> [ThreadSync; 2] {
276 let ccp = self.comp_config_ptr();
277 let ps = unsafe { addr_of!((*ccp).posted_signals) };
278 [
279 ThreadSync::new_sleep(ThreadSyncSleep::new(
280 ThreadSyncReference::Virtual(&*self.flags),
281 cur,
282 ThreadSyncOp::Equal,
283 ThreadSyncFlags::empty(),
284 )),
285 ThreadSync::new_sleep(ThreadSyncSleep::new(
286 ThreadSyncReference::Virtual(ps),
287 0,
288 ThreadSyncOp::Equal,
289 ThreadSyncFlags::empty(),
290 )),
291 ]
292 }
293
294 pub fn raw_flags(&self) -> u64 {
296 self.flags.load(Ordering::SeqCst)
297 }
298
299 pub(crate) fn start_main_thread(
300 &mut self,
301 state: u64,
302 tmgr: &mut ThreadMgr,
303 dynlink: &mut Context,
304 args: &[&CStr],
305 env: &[&CStr],
306 suspend_on_start: bool,
307 ) -> Option<bool> {
308 if self.has_flag(COMP_STARTED) {
309 return Some(false);
310 }
311 let state = state & !COMP_STARTED;
312 if self
313 .flags
314 .compare_exchange(
315 state,
316 state | COMP_STARTED,
317 Ordering::SeqCst,
318 Ordering::SeqCst,
319 )
320 .is_err()
321 {
322 return None;
323 }
324
325 tracing::debug!("starting main thread for compartment {}", self.name);
326 debug_assert!(self.main.is_none());
327 let (stack, entry, main_entry, ctors) = self.init_info.take().unwrap();
329 let mut build_init_info = || -> Option<_> {
330 let comp_config_info =
331 self.comp_config_object.get_comp_config() as *mut SharedCompConfig;
332 let ctors_in_comp = self.monitor_new_slice(&ctors).ok()?;
333
334 let mut args_in_comp: Vec<_> = args
336 .iter()
337 .map(|arg| self.monitor_new_slice(arg.to_bytes_with_nul()).unwrap())
338 .collect();
339
340 if args_in_comp.len() == 0 {
341 let cname = CString::new(self.name.as_bytes()).unwrap();
342 args_in_comp = vec![self.monitor_new_slice(cname.as_bytes()).unwrap()];
343 }
344 let argc = args_in_comp.len();
345
346 let mut envs_in_comp: Vec<_> = env
347 .iter()
348 .map(|arg| self.monitor_new_slice(arg.to_bytes_with_nul()).unwrap())
349 .collect();
350
351 args_in_comp.push(core::ptr::null_mut());
352 envs_in_comp.push(core::ptr::null_mut());
353
354 let args_in_comp_in_comp = self.monitor_new_slice(&args_in_comp).unwrap();
355 let envs_in_comp_in_comp = self.monitor_new_slice(&envs_in_comp).unwrap();
356
357 let comp_init_info = CompartmentInitInfo {
358 ctor_set_array: ctors_in_comp,
359 ctor_set_len: ctors.len(),
360 comp_config_info: comp_config_info.cast(),
361 };
362 let comp_init_info_in_comp = self.monitor_new(comp_init_info).ok()?;
363 let rtinfo = RuntimeInfo {
364 flags: 0,
365 kind: RUNTIME_INIT_COMP,
366 args: args_in_comp_in_comp.cast(),
367 argc,
368 entry: main_entry,
369 envp: envs_in_comp_in_comp.cast(),
370 init_info: InitInfoPtrs {
371 comp: comp_init_info_in_comp,
372 },
373 };
374 self.monitor_new(rtinfo).ok()
375 };
376 let arg = match build_init_info() {
377 Some(arg) => arg as usize,
378 None => {
379 self.set_flag(COMP_EXITED);
380 return None;
381 }
382 };
383 if self.build_tls_template(dynlink).is_none() {
384 self.set_flag(COMP_EXITED);
385 return None;
386 }
387 let mt = match CompThread::new(
388 tmgr,
389 dynlink,
390 stack,
391 self.instance,
392 Some(self.instance),
393 if main_entry != 0 { main_entry } else { entry },
394 arg,
395 suspend_on_start,
396 ) {
397 Ok(mt) => mt,
398 Err(_) => {
399 self.set_flag(COMP_EXITED);
400 return None;
401 }
402 };
403 self.main = Some(mt);
404 self.notify_state_changed();
405
406 Some(true)
407 }
408
409 fn build_tls_template(&mut self, dynlink: &mut Context) -> Option<()> {
410 let region = dynlink
411 .get_compartment_mut(self.compartment_id)
412 .unwrap()
413 .build_tls_region(RuntimeThreadControl::default(), |layout| {
414 unsafe { self.alloc.malloc(layout) }.ok()
415 })
416 .ok()?;
417
418 let template: TlsTemplateInfo = region.into();
419 let tls_template = self.monitor_new(template).ok()?;
420
421 let config = self.comp_config_object.read_comp_config();
422 config.set_tls_template(tls_template);
423 self.comp_config_object.write_config(config);
424 Some(())
425 }
426
427 #[allow(dead_code)]
428 pub fn read_error_code(&self) -> u64 {
429 let Some(ref main) = self.main else {
430 return 0;
431 };
432 main.thread.repr.get_repr().get_code()
433 }
434
435 pub fn get_nth_thread_info(&self, n: usize) -> Option<ThreadInfo> {
436 let Some(ref main) = self.main else {
437 return None;
438 };
439 if n == 0 {
440 return Some(ThreadInfo {
441 repr_id: main.thread.id,
442 });
443 }
444 self.per_thread
445 .keys()
446 .filter(|t| **t != main.thread.id)
447 .nth(n - 1)
448 .map(|id| ThreadInfo { repr_id: *id })
449 }
450
451 pub fn main_thread(&self) -> &Option<CompThread> {
452 &self.main
453 }
454
455 pub fn upcall_handle(
456 &self,
457 frame: &mut UpcallFrame,
458 info: &UpcallData,
459 ) -> Result<Option<ResumeFlags>, TwzError> {
460 let flags = if self.is_debugging {
461 tracing::info!("got monitor upcall {:?} {:?}", frame, info);
462 Some(ResumeFlags::SUSPEND)
463 } else {
464 tracing::warn!(
465 "supervisor exception in {}, thread {}: {:?}",
466 self.name,
467 info.thread_id,
468 info.info
469 );
470 None
471 };
472 Ok(flags)
473 }
474
475 pub(crate) fn inc_use_count(&mut self) {
476 self.use_count += 1;
477 tracing::trace!(
478 "compartment {} inc use count -> {}",
479 self.name,
480 self.use_count
481 );
482 }
483
484 pub(crate) fn dec_use_count(&mut self) -> bool {
485 debug_assert!(self.use_count > 0);
486 self.use_count -= 1;
487
488 tracing::trace!(
489 "compartment {} dec use count -> {}",
490 self.name,
491 self.use_count
492 );
493 let z = self.use_count == 0;
494 if z {
495 self.set_flag(COMP_THREAD_CAN_EXIT);
496 }
497 z
498 }
499}