1#![feature(naked_functions)]
6#![feature(linkage)]
7#![feature(result_flattening)]
8#![feature(thread_local)]
9#![feature(pointer_is_aligned_to)]
10#![feature(tuple_trait)]
11use std::{
12 alloc::Layout,
13 cell::UnsafeCell,
14 marker::{PhantomData, Tuple},
15 ptr::NonNull,
16 sync::{
17 atomic::{AtomicPtr, AtomicU32, Ordering},
18 OnceLock,
19 },
20};
21
22pub use dynlink::{
23 context::NewCompartmentFlags,
24 tls::{Tcb, TlsRegion},
25};
26use secgate::{
27 util::{Descriptor, Handle},
28 Crossing, DynamicSecGate,
29};
30use twizzler_abi::object::{ObjID, MAX_SIZE, NULLPAGE_SIZE};
31
32#[allow(unused_imports, unused_variables, unexpected_cfgs)]
33mod gates {
34 include! {"../../monitor/secapi/gates.rs"}
35}
36
37pub use gates::*;
38use twizzler_rt_abi::{
39 debug::{DlPhdrInfo, LinkMap, LoadedImageId},
40 error::{ArgumentError, TwzError},
41};
42
43#[repr(C)]
46pub struct SharedCompConfig {
47 pub sctx: ObjID,
50 tls_template: AtomicPtr<TlsTemplateInfo>,
52 pub root_library_id: Option<LoadedImageId>,
54}
55
56struct CompConfigFinder {
57 config: *const SharedCompConfig,
58}
59
60unsafe impl Sync for CompConfigFinder {}
63unsafe impl Send for CompConfigFinder {}
64
65static COMP_CONFIG: OnceLock<CompConfigFinder> = OnceLock::new();
66
67pub fn get_comp_config() -> &'static SharedCompConfig {
69 unsafe {
70 COMP_CONFIG
71 .get_or_init(|| CompConfigFinder {
72 config: monitor_rt_get_comp_config().unwrap() as *const _,
73 })
74 .config
75 .as_ref()
76 .unwrap()
77 }
78}
79
80pub fn set_comp_config(cfg: &'static SharedCompConfig) -> Result<(), ()> {
84 COMP_CONFIG
85 .set(CompConfigFinder { config: cfg })
86 .map_err(|_| ())
87}
88
89#[repr(C)]
91#[derive(Clone, Copy, Debug)]
92pub struct TlsTemplateInfo {
93 pub gen: u64,
94 pub layout: Layout,
95 pub alloc_base: NonNull<u8>,
96 pub tp_offset: usize,
97 pub dtv_offset: usize,
98 pub num_dtv_entries: usize,
99 pub module_top_offset: usize,
100}
101
102unsafe impl Send for TlsTemplateInfo {}
105unsafe impl Sync for TlsTemplateInfo {}
106
107impl From<TlsRegion> for TlsTemplateInfo {
108 fn from(value: TlsRegion) -> Self {
109 let offset = |ptr: NonNull<u8>| -> usize {
110 unsafe {
111 ptr.as_ptr()
112 .byte_offset_from(value.alloc_base.as_ptr())
113 .try_into()
114 .unwrap()
115 }
116 };
117 Self {
118 gen: value.gen,
119 layout: value.layout,
120 alloc_base: value.alloc_base,
121 num_dtv_entries: value.num_dtv_entries,
122 tp_offset: offset(value.thread_pointer),
123 dtv_offset: offset(value.dtv.cast()),
124 module_top_offset: offset(value.module_top),
125 }
126 }
127}
128
129impl TlsTemplateInfo {
130 pub unsafe fn init_new_tls_region<T>(&self, new: *mut u8, tcb_data: T) -> *mut Tcb<T> {
135 assert!(new.is_aligned_to(self.layout.align()));
136 core::ptr::copy_nonoverlapping(self.alloc_base.as_ptr(), new, self.layout.size());
138
139 let tcb = new.add(self.tp_offset) as *mut Tcb<T>;
140 let dtv_ptr = new.add(self.dtv_offset) as *mut *mut u8;
141 let dtv = core::slice::from_raw_parts_mut(dtv_ptr, self.num_dtv_entries);
142
143 for entry in dtv.iter_mut().skip(1) {
146 let offset = (*entry).byte_offset_from(self.alloc_base.as_ptr());
147 *entry = new.byte_offset(offset);
148 }
149
150 let dtv_0 = dtv_ptr as *mut u64;
152 *dtv_0 = self.gen;
153
154 {
156 let tcb = tcb.as_mut().unwrap();
157 tcb.dtv = dtv_ptr as *const usize;
158 tcb.self_ptr = tcb;
159 tcb.runtime_data = tcb_data;
160 }
161
162 tcb
163 }
164}
165
166impl SharedCompConfig {
167 pub fn new(sctx: ObjID, tls_template: *mut TlsTemplateInfo) -> Self {
168 Self {
169 sctx,
170 tls_template: AtomicPtr::new(tls_template),
171 root_library_id: None,
172 }
173 }
174
175 pub fn set_tls_template(&self, ptr: *mut TlsTemplateInfo) {
177 self.tls_template.store(ptr, Ordering::SeqCst);
178 }
179
180 pub fn get_tls_template(&self) -> *const TlsTemplateInfo {
182 self.tls_template.load(Ordering::SeqCst)
183 }
184}
185
186pub use gates::LibraryInfo as LibraryInfoRaw;
187
188#[derive(Debug)]
190pub struct LibraryInfo<'a> {
191 pub name: String,
193 pub compartment_id: ObjID,
195 pub objid: ObjID,
197 pub start: *const u8,
199 pub len: usize,
201 pub dl_info: DlPhdrInfo,
203 pub link_map: LinkMap,
205 pub slot: usize,
207 _pd: PhantomData<&'a ()>,
208 internal_name: Vec<u8>,
209}
210
211impl<'a> LibraryInfo<'a> {
212 fn from_raw(raw: LibraryInfoRaw) -> Self {
213 let name = lazy_sb::read_bytes_from_sb(raw.name_len);
214 let mut this = Self {
215 name: lazy_sb::read_string_from_sb(raw.name_len),
216 compartment_id: raw.compartment_id,
217 objid: raw.objid,
218 start: raw.start,
219 len: raw.len,
220 dl_info: raw.dl_info,
221 slot: raw.slot,
222 _pd: PhantomData,
223 internal_name: name,
224 link_map: raw.link_map,
225 };
226 this.dl_info.name = this.internal_name.as_ptr().cast();
227 this
228 }
229}
230
231#[derive(Debug)]
233pub struct LibraryHandle {
234 desc: Descriptor,
235}
236
237impl LibraryHandle {
238 pub fn info(&self) -> LibraryInfo<'_> {
240 LibraryInfo::from_raw(gates::monitor_rt_get_library_info(self.desc).unwrap())
241 }
242
243 pub fn desc(&self) -> Descriptor {
245 self.desc
246 }
247}
248
249pub struct LibraryLoader<'a> {
251 id: ObjID,
252 comp: Option<&'a CompartmentHandle>,
253}
254
255impl<'a> LibraryLoader<'a> {
256 pub fn new(id: ObjID) -> Self {
258 Self { id, comp: None }
259 }
260
261 pub fn in_compartment(&'a mut self, comp: &'a CompartmentHandle) -> &'a mut Self {
263 self.comp = Some(comp);
264 self
265 }
266
267 pub fn load(&self) -> Result<LibraryHandle, TwzError> {
269 let desc: Descriptor =
270 gates::monitor_rt_load_library(self.comp.map(|comp| comp.desc).flatten(), self.id)?;
271 Ok(LibraryHandle { desc })
272 }
273}
274
275pub struct CompartmentHandle {
277 desc: Option<Descriptor>,
278}
279
280impl CompartmentHandle {
281 pub fn info(&self) -> CompartmentInfo<'_> {
283 CompartmentInfo::from_raw(gates::monitor_rt_get_compartment_info(self.desc).unwrap())
284 }
285
286 pub fn desc(&self) -> Option<Descriptor> {
288 self.desc
289 }
290
291 pub unsafe fn dynamic_gate<A: Tuple + Crossing + Copy, R: Crossing + Copy>(
292 &self,
293 name: &str,
294 ) -> Result<DynamicSecGate<'_, A, R>, TwzError> {
295 let name_len = lazy_sb::write_bytes_to_sb(name.as_bytes());
296 let address = gates::monitor_rt_compartment_dynamic_gate(self.desc, name_len)?;
297 Ok(DynamicSecGate::new(address))
298 }
299}
300
301pub struct CompartmentLoader {
303 name: String,
304 args: Vec<String>,
305 env: Option<Vec<String>>,
306 flags: NewCompartmentFlags,
307}
308
309impl CompartmentLoader {
310 pub fn new(
312 compname: impl ToString,
313 libname: impl ToString,
314 flags: NewCompartmentFlags,
315 ) -> Self {
316 Self {
317 name: format!("{}::{}", compname.to_string(), libname.to_string()),
318 flags,
319 env: None,
320 args: vec![],
321 }
322 }
323
324 pub fn args<S: ToString>(&mut self, args: impl IntoIterator<Item = S>) -> &mut Self {
326 for arg in args.into_iter() {
327 self.args.push(arg.to_string())
328 }
329 self
330 }
331
332 pub fn env<S: ToString>(&mut self, env: impl IntoIterator<Item = S>) -> &mut Self {
334 self.env = Some(env.into_iter().map(|s| s.to_string()).collect());
335 self
336 }
337
338 pub fn load(&self) -> Result<CompartmentHandle, TwzError> {
340 fn get_current_env() -> Vec<String> {
341 std::env::vars()
342 .map(|(var, val)| format!("{}={}", var, val))
343 .collect()
344 }
345 let name_len = self.name.as_bytes().len();
346 let args_len = self
347 .args
348 .iter()
349 .fold(0, |acc, arg| acc + arg.as_bytes().len() + 1);
350 let env = self.env.clone().unwrap_or_else(|| get_current_env());
351 let envs_len = env
352 .iter()
353 .fold(0, |acc, arg| acc + arg.as_bytes().len() + 1);
354 let mut bytes = self.name.as_bytes().to_vec();
355 for arg in &self.args {
356 bytes.extend_from_slice(arg.as_bytes());
357 bytes.push(0);
358 }
359 for env in env {
360 bytes.extend_from_slice(env.as_bytes());
361 bytes.push(0);
362 }
363 let len = lazy_sb::write_bytes_to_sb(&bytes);
364 if len < envs_len + args_len + name_len {
365 return Err(ArgumentError::InvalidArgument.into());
366 }
367 let desc = gates::monitor_rt_load_compartment(
368 name_len as u64,
369 args_len as u64,
370 envs_len as u64,
371 self.flags.bits(),
372 )?;
373 Ok(CompartmentHandle { desc: Some(desc) })
374 }
375}
376
377impl Handle for CompartmentHandle {
378 type OpenError = TwzError;
379
380 type OpenInfo = ObjID;
381
382 fn open(info: Self::OpenInfo) -> Result<Self, Self::OpenError>
383 where
384 Self: Sized,
385 {
386 let desc = gates::monitor_rt_get_compartment_handle(info)?;
387 Ok(CompartmentHandle { desc: Some(desc) })
388 }
389
390 fn release(&mut self) {
391 if let Some(desc) = self.desc {
392 let _ = gates::monitor_rt_drop_compartment_handle(desc);
393 }
394 }
395}
396
397impl Drop for CompartmentHandle {
398 fn drop(&mut self) {
399 self.release();
400 }
401}
402
403impl Handle for LibraryHandle {
404 type OpenError = TwzError;
405
406 type OpenInfo = (Option<Descriptor>, usize);
407
408 fn open(info: Self::OpenInfo) -> Result<Self, Self::OpenError>
409 where
410 Self: Sized,
411 {
412 let desc = gates::monitor_rt_get_library_handle(info.0, info.1)?;
413 Ok(LibraryHandle { desc })
414 }
415
416 fn release(&mut self) {
417 let _ = gates::monitor_rt_drop_library_handle(self.desc);
418 }
419}
420
421impl Drop for LibraryHandle {
422 fn drop(&mut self) {
423 self.release()
424 }
425}
426
427#[derive(Clone, Debug)]
429pub struct CompartmentInfo<'a> {
430 pub name: String,
432 pub id: ObjID,
434 pub sctx: ObjID,
436 pub flags: CompartmentFlags,
438 pub nr_libs: usize,
440 _pd: PhantomData<&'a ()>,
441}
442
443impl<'a> CompartmentInfo<'a> {
444 fn from_raw(raw: gates::CompartmentInfo) -> Self {
445 Self {
446 name: lazy_sb::read_string_from_sb(raw.name_len),
447 id: raw.id,
448 sctx: raw.sctx,
449 flags: CompartmentFlags::from_bits_truncate(raw.flags),
450 nr_libs: raw.nr_libs,
451 _pd: PhantomData,
452 }
453 }
454}
455
456impl CompartmentHandle {
457 pub fn current() -> Self {
459 Self { desc: None }
460 }
461
462 pub fn lookup(name: impl AsRef<str>) -> Result<Self, TwzError> {
464 let name_len = lazy_sb::write_bytes_to_sb(name.as_ref().as_bytes());
465 Ok(Self {
466 desc: Some(gates::monitor_rt_lookup_compartment(name_len)?),
467 })
468 }
469
470 pub fn deps(&self) -> CompartmentDepsIter {
472 CompartmentDepsIter::new(self)
473 }
474
475 pub fn root(&self) -> LibraryHandle {
477 self.libs().next().unwrap()
478 }
479
480 pub fn libs(&self) -> LibraryIter<'_> {
482 LibraryIter::new(self)
483 }
484
485 pub fn threads(&self) -> CompartmentThreadsIter<'_> {
487 CompartmentThreadsIter::new(self)
488 }
489
490 pub fn wait(&self, flags: CompartmentFlags) -> CompartmentFlags {
491 CompartmentFlags::from_bits_truncate(
492 gates::monitor_rt_compartment_wait(self.desc(), flags.bits()).unwrap(),
493 )
494 }
495}
496
497pub struct LibraryIter<'a> {
499 n: usize,
500 comp: &'a CompartmentHandle,
501}
502
503impl<'a> LibraryIter<'a> {
504 fn new(comp: &'a CompartmentHandle) -> Self {
505 Self { n: 0, comp }
506 }
507}
508
509impl<'a> Iterator for LibraryIter<'a> {
510 type Item = LibraryHandle;
511
512 fn next(&mut self) -> Option<Self::Item> {
513 let handle = LibraryHandle::open((self.comp.desc, self.n)).ok();
514 if handle.is_some() {
515 self.n += 1;
516 }
517 handle
518 }
519}
520
521pub struct CompartmentDepsIter<'a> {
523 n: usize,
524 comp: &'a CompartmentHandle,
525}
526
527impl<'a> CompartmentDepsIter<'a> {
528 fn new(comp: &'a CompartmentHandle) -> Self {
529 Self { n: 0, comp }
530 }
531}
532
533impl<'a> Iterator for CompartmentDepsIter<'a> {
534 type Item = CompartmentHandle;
535
536 fn next(&mut self) -> Option<Self::Item> {
537 let desc = gates::monitor_rt_get_compartment_deps(self.comp.desc, self.n).ok()?;
538 self.n += 1;
539 Some(CompartmentHandle { desc: Some(desc) })
540 }
541
542 fn nth(&mut self, n: usize) -> Option<Self::Item> {
543 self.n += n;
544 self.next()
545 }
546}
547
548pub struct CompartmentThreadsIter<'a> {
550 n: usize,
551 comp: &'a CompartmentHandle,
552}
553
554impl<'a> CompartmentThreadsIter<'a> {
555 fn new(comp: &'a CompartmentHandle) -> Self {
556 Self { n: 0, comp }
557 }
558}
559
560impl<'a> Iterator for CompartmentThreadsIter<'a> {
561 type Item = ThreadInfo;
562
563 fn next(&mut self) -> Option<Self::Item> {
564 let info = gates::monitor_rt_get_compartment_thread(self.comp.desc, self.n).ok()?;
565 self.n += 1;
566 Some(info)
567 }
568
569 fn nth(&mut self, n: usize) -> Option<Self::Item> {
570 self.n += n;
571 self.next()
572 }
573}
574
575bitflags::bitflags! {
576 #[derive(Clone, Debug, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
578 pub struct CompartmentFlags : u64 {
579 const READY = 0x1;
581 const IS_BINARY = 0x2;
583 const THREAD_CAN_EXIT = 0x4;
585 const STARTED = 0x8;
587 const DESTRUCTED = 0x10;
589 const EXITED = 0x20;
591 }
592}
593
594#[derive(Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Debug)]
596pub struct MappedObjectAddrs {
597 pub slot: usize,
598 pub start: usize,
599 pub meta: usize,
600}
601
602impl MappedObjectAddrs {
603 pub fn new(slot: usize) -> Self {
604 Self {
605 start: slot * MAX_SIZE,
606 meta: (slot + 1) * MAX_SIZE - NULLPAGE_SIZE,
607 slot,
608 }
609 }
610}
611
612pub fn stats() -> Option<gates::MonitorStats> {
614 gates::monitor_rt_stats().ok()
615}
616
617mod lazy_sb {
618 use std::cell::{OnceCell, RefCell};
622
623 use secgate::util::SimpleBuffer;
624 use twizzler_rt_abi::object::MapFlags;
625
626 struct LazyThreadSimpleBuffer {
627 sb: OnceCell<SimpleBuffer>,
628 }
629
630 impl LazyThreadSimpleBuffer {
631 const fn new() -> Self {
632 Self {
633 sb: OnceCell::new(),
634 }
635 }
636
637 fn init() -> SimpleBuffer {
638 let id = super::gates::monitor_rt_get_thread_simple_buffer()
639 .expect("failed to get per-thread monitor simple buffer");
640 let oh =
641 twizzler_rt_abi::object::twz_rt_map_object(id, MapFlags::READ | MapFlags::WRITE)
642 .unwrap();
643 SimpleBuffer::new(oh)
644 }
645
646 fn read(&mut self, buf: &mut [u8]) -> usize {
647 let sb = self.sb.get_or_init(|| Self::init());
648 sb.read(buf)
649 }
650
651 fn write(&mut self, buf: &[u8]) -> usize {
652 if self.sb.get().is_none() {
653 self.sb.set(Self::init()).unwrap();
655 }
656 let sb = self.sb.get_mut().unwrap();
657 sb.write(buf)
658 }
659 }
660
661 #[thread_local]
662 static LAZY_SB: RefCell<LazyThreadSimpleBuffer> = RefCell::new(LazyThreadSimpleBuffer::new());
663
664 pub(super) fn read_string_from_sb(len: usize) -> String {
665 let mut buf = vec![0u8; len];
666 let len = LAZY_SB.borrow_mut().read(&mut buf);
667 String::from_utf8_lossy(&buf[0..len]).to_string()
668 }
669
670 pub(super) fn read_bytes_from_sb(len: usize) -> Vec<u8> {
671 let mut buf = vec![0u8; len];
672 let len = LAZY_SB.borrow_mut().read(&mut buf);
673 buf.truncate(len);
674 buf
675 }
676
677 pub(super) fn write_bytes_to_sb(buf: &[u8]) -> usize {
678 LAZY_SB.borrow_mut().write(buf)
679 }
680}
681
682pub const THREAD_STARTED: u32 = 1;
683pub struct RuntimeThreadControl {
684 pub id: UnsafeCell<u32>,
685 pub did_exit: UnsafeCell<u32>,
686 pub internal_lock: AtomicU32,
688 pub flags: AtomicU32,
689 pub stack_canary: u64,
690 pub libc_data: [u64; 1000],
691}
692
693impl Default for RuntimeThreadControl {
694 fn default() -> Self {
695 Self::new(0)
696 }
697}
698
699impl RuntimeThreadControl {
700 pub const fn new(id: u32) -> Self {
701 Self {
702 internal_lock: AtomicU32::new(0),
703 flags: AtomicU32::new(0),
704 id: UnsafeCell::new(id),
705 did_exit: UnsafeCell::new(0),
706 stack_canary: 0,
707 libc_data: [0; 1000],
708 }
709 }
710
711 fn write_lock(&self) {
712 loop {
713 let old = self.internal_lock.fetch_or(1, Ordering::Acquire);
714 if old == 0 {
715 break;
716 }
717 }
718 }
719
720 fn write_unlock(&self) {
721 self.internal_lock.fetch_and(!1, Ordering::Release);
722 }
723
724 fn read_lock(&self) {
725 loop {
726 let old = self.internal_lock.fetch_add(2, Ordering::Acquire);
727 if old > i32::MAX as u32 {
729 twizzler_rt_abi::core::twz_rt_abort();
730 }
731 if old & 1 == 0 {
732 break;
733 }
734 }
735 }
736
737 fn read_unlock(&self) {
738 self.internal_lock.fetch_sub(2, Ordering::Release);
739 }
740
741 pub fn set_id(&self, id: u32) {
742 self.write_lock();
743 unsafe {
744 *self.id.get().as_mut().unwrap() = id;
745 }
746 self.write_unlock();
747 }
748
749 pub fn id(&self) -> u32 {
750 self.read_lock();
751 let id = unsafe { *self.id.get().as_ref().unwrap() };
752 self.read_unlock();
753 id
754 }
755}
756
757pub fn set_nameroot(root: ObjID) -> Result<(), TwzError> {
758 gates::monitor_rt_set_nameroot(root)
759}