1#![feature(fn_traits)]
2#![feature(unboxed_closures)]
3#![feature(tuple_trait)]
4#![feature(auto_traits)]
5#![feature(negative_impls)]
6#![feature(linkage)]
7#![feature(maybe_uninit_as_bytes)]
8#![feature(thread_local)]
9
10use core::ffi::{c_char, CStr};
11use std::{
12 cell::{RefCell, UnsafeCell},
13 fmt::Debug,
14 marker::{PhantomData, Tuple},
15 mem::MaybeUninit,
16 sync::OnceLock,
17};
18
19pub use secgate_macros::*;
20use twizzler_abi::object::ObjID;
21pub use twizzler_rt_abi::error::{ResourceError, TwzError};
22
23pub mod util;
24
25#[repr(C)]
29pub struct SecGateInfo<F> {
30 pub imp: F,
33 name: *const c_char,
35}
36
37impl<F> core::fmt::Debug for SecGateInfo<F> {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 write!(f, "SecGateInfo({:p})", self.name)
40 }
41}
42
43impl<F> SecGateInfo<F> {
44 pub const fn new(imp: F, name: &'static CStr) -> Self {
45 Self {
46 imp,
47 name: name.as_ptr(),
48 }
49 }
50
51 pub fn name(&self) -> &CStr {
52 unsafe { CStr::from_ptr(self.name) }
54 }
55}
56
57unsafe impl<F: Send> Send for SecGateInfo<F> {}
60unsafe impl<F: Sync> Sync for SecGateInfo<F> {}
63
64pub const SECGATE_TRAMPOLINE_ALIGN: usize = 0x10;
66
67pub type RawSecGateInfo = SecGateInfo<usize>;
69static_assertions::assert_eq_size!(RawSecGateInfo, SecGateInfo<&fn()>);
71
72#[derive(Clone, Copy)]
75#[repr(C)]
76pub struct Arguments<Args: Tuple + Crossing + Copy> {
77 args: Args,
78}
79
80impl<Args: Tuple + Crossing + Copy> Arguments<Args> {
81 pub fn with_alloca<F, R>(args: Args, f: F) -> R
82 where
83 F: FnOnce(&mut Self) -> R,
84 {
85 alloca::alloca(|stack_space| {
86 stack_space.write(Self { args });
87 f(unsafe { stack_space.assume_init_mut() })
89 })
90 }
91
92 pub fn into_inner(self) -> Args {
93 self.args
94 }
95}
96
97#[derive(Copy)]
100#[repr(C)]
101pub struct Return<T: Crossing + Copy> {
102 isset: bool,
103 ret: MaybeUninit<T>,
104}
105
106impl<T: Copy + Crossing> Clone for Return<T> {
107 fn clone(&self) -> Self {
108 *self
109 }
110}
111
112impl<T: Crossing + Copy> Return<T> {
113 pub fn with_alloca<F, R>(f: F) -> R
114 where
115 F: FnOnce(&mut Self) -> R,
116 {
117 alloca::alloca(|stack_space| {
118 stack_space.write(Self {
119 isset: false,
120 ret: MaybeUninit::uninit(),
121 });
122 f(unsafe { stack_space.assume_init_mut() })
124 })
125 }
126
127 pub fn into_inner(self) -> Option<T> {
130 if self.isset {
131 Some(unsafe { self.ret.assume_init() })
132 } else {
133 None
134 }
135 }
136
137 pub fn new_uninit() -> Self {
139 Self {
140 isset: false,
141 ret: MaybeUninit::uninit(),
142 }
143 }
144
145 pub fn set(&mut self, val: T) {
147 self.ret.write(val);
148 self.isset = true;
149 }
150}
151
152pub unsafe auto trait Crossing {}
159
160impl<T> !Crossing for &T {}
161impl<T> !Crossing for &mut T {}
162impl<T: ?Sized> !Crossing for UnsafeCell<T> {}
163impl<T> !Crossing for *const T {}
164impl<T> !Crossing for *mut T {}
165impl<T> !Crossing for &[T] {}
166impl<T> !Crossing for &mut [T] {}
167
168unsafe impl<T: Crossing + Copy> Crossing for Result<T, TwzError> {}
169
170#[macro_export]
175macro_rules! secgate_prelude {
176 () => {
177 #[link(name = "calloca", kind = "static")]
178 extern "C" {
179 pub fn c_with_alloca();
180 }
181 };
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
185#[repr(C)]
186pub struct GateCallInfo {
187 thread_id: ObjID,
188 src_ctx: ObjID,
189}
190
191impl GateCallInfo {
192 pub fn with_alloca<F, R>(thread_id: ObjID, src_ctx: ObjID, f: F) -> R
194 where
195 F: FnOnce(&mut Self) -> R,
196 {
197 alloca::alloca(|stack_space| {
198 stack_space.write(Self { thread_id, src_ctx });
199 f(unsafe { stack_space.assume_init_mut() })
201 })
202 }
203
204 pub fn source_context(&self) -> Option<ObjID> {
206 if self.src_ctx.raw() == 0 {
207 None
208 } else {
209 Some(self.src_ctx)
210 }
211 }
212
213 pub fn thread_id(&self) -> ObjID {
215 if self.thread_id.raw() == 0 {
216 twizzler_abi::syscall::sys_thread_self_id()
217 } else {
218 self.thread_id
219 }
220 }
221
222 pub fn canonicalize(self) -> Self {
224 Self {
225 thread_id: self.thread_id(),
226 src_ctx: self.src_ctx,
227 }
228 }
229}
230
231fn get_tp() -> usize {
232 let mut val: usize;
233 unsafe {
234 #[cfg(target_arch = "x86_64")]
235 core::arch::asm!("rdfsbase {}", out(reg) val);
236 #[cfg(not(target_arch = "x86_64"))]
237 core::arch::asm!("mrs {}, tpidr_el0", out(reg) val);
238 }
239 val
240}
241
242pub fn get_thread_id() -> ObjID {
244 if !unsafe { __is_monitor_ready() } {
245 return twizzler_abi::syscall::sys_thread_self_id();
246 }
247 #[thread_local]
248 static ONCE_ID: OnceLock<ObjID> = OnceLock::new();
249 if get_tp() != 0 {
250 *ONCE_ID.get_or_init(|| twizzler_abi::syscall::sys_thread_self_id())
251 } else {
252 twizzler_abi::syscall::sys_thread_self_id()
253 }
254}
255
256pub fn get_sctx_id() -> ObjID {
258 if !unsafe { __is_monitor_ready() } {
259 return twizzler_abi::syscall::sys_thread_active_sctx_id();
260 }
261 #[thread_local]
262 static ONCE_ID: OnceLock<ObjID> = OnceLock::new();
263 if get_tp() != 0 {
264 *ONCE_ID.get_or_init(|| twizzler_abi::syscall::sys_thread_active_sctx_id())
265 } else {
266 twizzler_abi::syscall::sys_thread_active_sctx_id()
267 }
268}
269
270pub fn runtime_preentry(info: &GateCallInfo) -> Result<(), TwzError> {
271 twizzler_rt_abi::core::twz_rt_cross_compartment_entry()?;
272 set_caller(info.clone());
273 Ok(())
274}
275
276pub struct SecFrame {
277 tp: usize,
278 sctx: ObjID,
279}
280
281pub fn frame() -> SecFrame {
282 let tp = get_tp();
283 let sctx = twizzler_abi::syscall::sys_thread_active_sctx_id();
285 SecFrame { tp, sctx }
286}
287
288pub fn restore_frame(frame: SecFrame) {
289 if frame.tp != 0 {
290 twizzler_abi::syscall::sys_thread_settls(frame.tp as u64);
291 }
292 twizzler_abi::syscall::sys_thread_set_active_sctx_id(frame.sctx).unwrap();
293}
294
295#[derive(Clone, Copy)]
296pub struct DynamicSecGate<'comp, A, R> {
297 address: usize,
298 _pd: PhantomData<&'comp (A, R)>,
299}
300
301impl<'a, A: Tuple + Crossing + Copy, R: Crossing + Copy> Fn<A> for DynamicSecGate<'a, A, R> {
302 extern "rust-call" fn call(&self, args: A) -> Self::Output {
303 unsafe { dynamic_gate_call(*self, args) }
304 }
305}
306
307impl<'a, A: Tuple + Crossing + Copy, R: Crossing + Copy> FnMut<A> for DynamicSecGate<'a, A, R> {
308 extern "rust-call" fn call_mut(&mut self, args: A) -> Self::Output {
309 unsafe { dynamic_gate_call(*self, args) }
310 }
311}
312
313impl<'a, A: Tuple + Crossing + Copy, R: Crossing + Copy> FnOnce<A> for DynamicSecGate<'a, A, R> {
314 type Output = Result<R, TwzError>;
315
316 extern "rust-call" fn call_once(self, args: A) -> Self::Output {
317 unsafe { dynamic_gate_call(self, args) }
318 }
319}
320
321impl<'a, A, R> Debug for DynamicSecGate<'a, A, R> {
322 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323 write!(
324 f,
325 "DynamicSecGate [{} -> {}] {{ address: {:x} }}",
326 std::any::type_name::<A>(),
327 std::any::type_name::<R>(),
328 self.address
329 )
330 }
331}
332
333impl<'comp, A, R> DynamicSecGate<'comp, A, R> {
334 pub unsafe fn new(address: usize) -> Self {
335 Self {
336 address,
337 _pd: PhantomData,
338 }
339 }
340}
341
342pub unsafe fn dynamic_gate_call<A: Tuple + Crossing + Copy, R: Crossing + Copy>(
343 target: DynamicSecGate<A, R>,
344 args: A,
345) -> Result<R, TwzError> {
346 let frame = frame();
347 let ret = GateCallInfo::with_alloca(get_thread_id(), get_sctx_id(), |info| {
349 Arguments::<A>::with_alloca(args, |args| {
350 Return::<Result<R, TwzError>>::with_alloca(|ret| {
351 unsafe {
353 #[cfg(target_arch = "x86_64")]
355 core::arch::asm!("call {target}", target = in(reg) target.address, in("rdi") info as *const _, in("rsi") args as *const _, in("rdx") ret as *mut _, clobber_abi("C"));
356 #[cfg(not(target_arch = "x86_64"))]
357 todo!()
358 }
359 ret.into_inner()
360 })
361 })
362 });
363 restore_frame(frame);
364 ret.ok_or(ResourceError::Unavailable)?
365}
366
367#[thread_local]
368static CALLER_INFO: RefCell<Option<GateCallInfo>> = RefCell::new(None);
369
370unsafe extern "C" {
371 fn __is_monitor_ready() -> bool;
372}
373
374pub fn set_caller(info: GateCallInfo) {
375 if unsafe { __is_monitor_ready() } {
376 CALLER_INFO.borrow_mut().replace(info);
377 }
378}
379
380fn _reset_caller() {
381 if unsafe { __is_monitor_ready() } {
382 CALLER_INFO.borrow_mut().take();
383 }
384}
385
386pub fn get_caller() -> Option<GateCallInfo> {
387 if !unsafe { __is_monitor_ready() } {
388 return None;
389 }
390 if CALLER_INFO.borrow().is_none() {
391 panic!("..")
392 }
393 CALLER_INFO.borrow().clone()
394}