diff --git a/Cargo.lock b/Cargo.lock index 4a6b7317..bea3e82c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -955,6 +955,16 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "026ac6ceace6298d2c557ef5ed798894962296469ec7842288ea64674201a2d1" +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn 2.0.85", +] + [[package]] name = "cty" version = "0.2.2" @@ -3242,7 +3252,7 @@ dependencies = [ "twizzler-abi", "twizzler-object", "twizzler-rt-abi", - "twz-rt", + "twizzler-runtime", ] [[package]] @@ -3256,6 +3266,29 @@ dependencies = [ "twizzler-rt-abi", ] +[[package]] +name = "montest" +version = "0.1.0" +dependencies = [ + "ctor", + "monitor", + "montest-lib", + "secgate", + "tracing", + "tracing-subscriber", + "twizzler-abi", + "twizzler-runtime", +] + +[[package]] +name = "montest-lib" +version = "0.1.0" +dependencies = [ + "secgate", + "twizzler-abi", + "twizzler-runtime", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -4106,9 +4139,9 @@ dependencies = [ [[package]] name = "scoped-tls-hkt" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ddc765d3410d9f6c6ca071bf0b67f6b01e3ec4595dc3892f02677e75819dddc" +checksum = "e9603871ffe5df3ac39cb624790c296dbd47a400d202f56bf3e414045099524d" [[package]] name = "scopeguard" @@ -5038,6 +5071,14 @@ dependencies = [ "twizzler-types", ] +[[package]] +name = "twizzler-runtime" +version = "0.1.0" +dependencies = [ + "twizzler-rt-abi", + "twz-rt", +] + [[package]] name = "twizzler-types" version = "0.1.0" @@ -5065,6 +5106,7 @@ dependencies = [ "talc", "thiserror", "tracing", + "tracing-subscriber", "twizzler-abi", "twizzler-rt-abi", ] diff --git a/Cargo.toml b/Cargo.toml index 79ac425f..44334ce4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,10 @@ members = [ "src/runtime/secgate", "src/runtime/monitor", "src/runtime/monitor-api", + "src/runtime/monitor/tests/montest", + "src/runtime/monitor/tests/montest-lib", "src/runtime/rt", + "src/runtime/rt-impl", "src/runtime/minruntime", "src/abi/rt-abi", "src/abi/types", @@ -43,7 +46,9 @@ initrd = [ "crate:nettest", "crate:pager", "lib:twz-rt", - "lib:monitor", + "crate:monitor", + "crate:montest", + "lib:montest-lib", "crate:mnemosyne", "crate:stdfs_demo", #"third-party:hello-world-rs" diff --git a/src/abi b/src/abi index f1d1237e..a12afe7b 160000 --- a/src/abi +++ b/src/abi @@ -1 +1 @@ -Subproject commit f1d1237ee0b145fb403ebfea2f10ea77da240286 +Subproject commit a12afe7b3c5d9deedeb7311bfc85fae00a742f85 diff --git a/src/bin/bootstrap/src/main.rs b/src/bin/bootstrap/src/main.rs index b427be4f..a05548d1 100644 --- a/src/bin/bootstrap/src/main.rs +++ b/src/bin/bootstrap/src/main.rs @@ -68,12 +68,11 @@ fn name_resolver(mut name: &str) -> Result { fn start_runtime(_runtime_monitor: ObjID, _runtime_library: ObjID) -> ! { let engine = Engine; let mut ctx = dynlink::context::Context::new(Box::new(engine)); - let unlib = UnloadedLibrary::new("libmonitor.so"); + let unlib = UnloadedLibrary::new("monitor"); let monitor_comp_id = ctx .add_compartment("monitor", NewCompartmentFlags::EXPORT_GATES) .unwrap(); - info!("==> {}", monitor_comp_id); let monitor_id = ctx .load_library_in_compartment(monitor_comp_id, unlib, true) .unwrap()[0] @@ -90,11 +89,7 @@ fn start_runtime(_runtime_monitor: ObjID, _runtime_library: ObjID) -> ! { debug!("context loaded, prepping jump to monitor"); let entry = ctx - .lookup_symbol( - monitor_id, - "monitor_entry_from_bootstrap", - LookupFlags::empty(), - ) + .lookup_symbol(monitor_id, "_start", LookupFlags::empty()) .unwrap(); let value = entry.reloc_value() as usize; @@ -136,13 +131,13 @@ fn start_runtime(_runtime_monitor: ObjID, _runtime_library: ObjID) -> ! { extern crate twizzler_minruntime; fn main() { let subscriber = FmtSubscriber::builder() - .with_max_level(Level::DEBUG) + .with_max_level(Level::INFO) .finish(); tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); let runtime_lib = find_init_name("libtwz_rt.so").unwrap(); - let monitor = find_init_name("libmonitor.so").unwrap(); + let monitor = find_init_name("monitor").unwrap(); info!("bootstrapping runtime monitor"); start_runtime(monitor, runtime_lib); diff --git a/src/kernel/src/initrd.rs b/src/kernel/src/initrd.rs index de4c5eb9..dbb0513c 100644 --- a/src/kernel/src/initrd.rs +++ b/src/kernel/src/initrd.rs @@ -65,7 +65,7 @@ pub fn init(modules: &[BootModule]) { } let obj = Arc::new(obj); obj::register_object(obj.clone()); - if e.filename().as_str() == "init" { + if e.filename().as_str() == "bootstrap" { boot_objects.init = Some(obj.clone()); } boot_objects diff --git a/src/lib/twizzler-abi/src/upcall.rs b/src/lib/twizzler-abi/src/upcall.rs index f2f7e27d..18ee9cbd 100644 --- a/src/lib/twizzler-abi/src/upcall.rs +++ b/src/lib/twizzler-abi/src/upcall.rs @@ -147,9 +147,11 @@ pub struct UpcallTarget { impl UpcallTarget { /// Construct a new upcall target. pub fn new( - self_address: Option !>, + self_address: Option< + unsafe extern "C-unwind" fn(*mut core::ffi::c_void, *const core::ffi::c_void) -> !, + >, super_address: Option< - unsafe extern "C-unwind" fn(*mut UpcallFrame, *const UpcallData) -> !, + unsafe extern "C-unwind" fn(*mut core::ffi::c_void, *const core::ffi::c_void) -> !, >, super_stack: usize, super_stack_size: usize, diff --git a/src/runtime/README.md b/src/runtime/README.md new file mode 100644 index 00000000..5f3da661 --- /dev/null +++ b/src/runtime/README.md @@ -0,0 +1,11 @@ +# runtime subdirectory + +This subdirectory contains all the crates for the core runtime: + + - dynlink: the dynamic linker code + - minruntime: the minimal (no_std, static linking available) runtime + - monitor: the monitor implementation + - monitor-api: the API crate for interaction with the monitor from runtime or user programs + - rt: reference runtime wrapper crate + - rt-impl: the reference runtime implementation. Users should link against the wrapper crate (rt), not this one. + - secgate: secure gate types and utility functions diff --git a/src/runtime/dynlink/src/arch/x86_64.rs b/src/runtime/dynlink/src/arch/x86_64.rs index 667bf99c..0188cc03 100644 --- a/src/runtime/dynlink/src/arch/x86_64.rs +++ b/src/runtime/dynlink/src/arch/x86_64.rs @@ -3,9 +3,9 @@ use crate::tls::{Tcb, TlsRegion}; pub(crate) const MINIMUM_TLS_ALIGNMENT: usize = 32; pub use elf::abi::{ - R_X86_64_64 as REL_SYMBOLIC, R_X86_64_COPY as REL_COPY, R_X86_64_DTPMOD64 as REL_DTPMOD, - R_X86_64_DTPOFF64 as REL_DTPOFF, R_X86_64_GLOB_DAT as REL_GOT, R_X86_64_JUMP_SLOT as REL_PLT, - R_X86_64_RELATIVE as REL_RELATIVE, R_X86_64_TPOFF64 as REL_TPOFF, + R_X86_64_64 as REL_SYMBOLIC, R_X86_64_DTPMOD64 as REL_DTPMOD, R_X86_64_DTPOFF64 as REL_DTPOFF, + R_X86_64_GLOB_DAT as REL_GOT, R_X86_64_JUMP_SLOT as REL_PLT, R_X86_64_RELATIVE as REL_RELATIVE, + R_X86_64_TPOFF64 as REL_TPOFF, }; /// Get a pointer to the current thread control block, using the thread pointer. @@ -22,7 +22,7 @@ impl TlsRegion { /// Get a pointer to the thread control block for this TLS region. /// /// # Safety - /// The TCB must actually contain runtime data of type T, and be initialized. + /// The TCB must actually contain runtime data of type T, and be initialized. pub unsafe fn get_thread_control_block(&self) -> *mut Tcb { self.get_thread_pointer_value() as *mut _ } diff --git a/src/runtime/dynlink/src/compartment.rs b/src/runtime/dynlink/src/compartment.rs index 53fa0ce3..dfe35db9 100644 --- a/src/runtime/dynlink/src/compartment.rs +++ b/src/runtime/dynlink/src/compartment.rs @@ -7,7 +7,6 @@ use std::{ use petgraph::stable_graph::NodeIndex; use talc::{ErrOnOom, Talc}; -use tracing::info; use crate::{context::NewCompartmentFlags, engines::Backing, library::LibraryId, tls::TlsInfo}; @@ -87,6 +86,6 @@ impl Debug for Compartment { impl Drop for Compartment { fn drop(&mut self) { - info!("dynlink: drop compartment {:?}", self); + tracing::debug!("dynlink: drop compartment {:?}", self); } } diff --git a/src/runtime/dynlink/src/context.rs b/src/runtime/dynlink/src/context.rs index a6f81de7..56c41202 100644 --- a/src/runtime/dynlink/src/context.rs +++ b/src/runtime/dynlink/src/context.rs @@ -1,6 +1,6 @@ //! Management of global context. -use std::{collections::HashMap, fmt::Display, ops::Index}; +use std::{collections::HashMap, fmt::Display}; use petgraph::stable_graph::{NodeIndex, StableDiGraph}; use stable_vec::StableVec; diff --git a/src/runtime/dynlink/src/context/relocate.rs b/src/runtime/dynlink/src/context/relocate.rs index 0f9de1cf..b9f9a6ed 100644 --- a/src/runtime/dynlink/src/context/relocate.rs +++ b/src/runtime/dynlink/src/context/relocate.rs @@ -94,6 +94,7 @@ impl Context { } else { None }; + let sn = symbol.as_ref().map(|s| s.0.to_string()).unwrap_or_default(); // Helper for logging errors. let open_sym = || { @@ -150,15 +151,19 @@ impl Context { unsafe { *target = val.wrapping_add_signed(addend) } } REL_TPOFF => { - if let Some(tls) = lib.tls_id { - let val = open_sym().map(|sym| sym.raw_value()).unwrap_or(0); + let sym = open_sym()?; + if let Some(tls) = sym.lib.tls_id { unsafe { - *target = val + *target = sym + .raw_value() .wrapping_sub(tls.offset() as u64) .wrapping_add_signed(addend) } } else { - error!("{}: TPOFF relocations require a PT_TLS segment", lib); + error!( + "{}: TPOFF relocations require a PT_TLS segment (sym {})", + lib, sn + ); Err(DynlinkErrorKind::NoTLSInfo { library: lib.name.clone(), })? diff --git a/src/runtime/dynlink/src/library.rs b/src/runtime/dynlink/src/library.rs index 794cb72a..37bbff79 100644 --- a/src/runtime/dynlink/src/library.rs +++ b/src/runtime/dynlink/src/library.rs @@ -4,7 +4,6 @@ use std::fmt::{Debug, Display}; use elf::{ abi::{DT_FLAGS_1, PT_PHDR, PT_TLS, STB_WEAK}, - dynamic::Dyn, endian::NativeEndian, segment::{Elf64_Phdr, ProgramHeader}, ParseError, @@ -351,7 +350,7 @@ impl Debug for Library { impl Drop for Library { fn drop(&mut self) { - tracing::info!("dynlink: drop library: {:?}", self); + tracing::debug!("dynlink: drop library: {:?}", self); } } diff --git a/src/runtime/minruntime/src/arch/aarch64/upcall.rs b/src/runtime/minruntime/src/arch/aarch64/upcall.rs index 82c96a5f..c0e1f107 100644 --- a/src/runtime/minruntime/src/arch/aarch64/upcall.rs +++ b/src/runtime/minruntime/src/arch/aarch64/upcall.rs @@ -12,8 +12,8 @@ pub(crate) unsafe extern "C" fn upcall_entry2( #[no_mangle] pub(crate) unsafe extern "C-unwind" fn upcall_entry( - frame: *mut UpcallFrame, - data: *const UpcallData, + frame: *mut core::ffi::c_void, + data: *const core::ffi::c_void, ) -> ! { core::arch::asm!( "b upcall_entry2", diff --git a/src/runtime/minruntime/src/arch/x86_64/upcall.rs b/src/runtime/minruntime/src/arch/x86_64/upcall.rs index 6cf0ce6d..80a4540c 100644 --- a/src/runtime/minruntime/src/arch/x86_64/upcall.rs +++ b/src/runtime/minruntime/src/arch/x86_64/upcall.rs @@ -12,9 +12,10 @@ pub(crate) unsafe extern "C" fn upcall_entry2( #[no_mangle] pub(crate) unsafe extern "C-unwind" fn upcall_entry( - rdi: *mut UpcallFrame, - rsi: *const UpcallData, + rdi: *mut core::ffi::c_void, + rsi: *const core::ffi::c_void, ) -> ! { + let rdi: *const UpcallFrame = rdi.cast(); core::arch::asm!( ".cfi_signal_frame", "mov rbp, rdx", diff --git a/src/runtime/monitor-api/src/lib.rs b/src/runtime/monitor-api/src/lib.rs index 7f9c1c5b..02587076 100644 --- a/src/runtime/monitor-api/src/lib.rs +++ b/src/runtime/monitor-api/src/lib.rs @@ -8,10 +8,11 @@ #![feature(pointer_is_aligned_to)] use std::{ alloc::Layout, + cell::UnsafeCell, marker::PhantomData, ptr::NonNull, sync::{ - atomic::{AtomicPtr, Ordering}, + atomic::{AtomicPtr, AtomicU32, Ordering}, OnceLock, }, }; @@ -293,6 +294,8 @@ impl CompartmentHandle { /// A builder-type for loading compartments. pub struct CompartmentLoader { name: String, + args: Vec, + env: Option>, flags: NewCompartmentFlags, } @@ -306,16 +309,63 @@ impl CompartmentLoader { Self { name: format!("{}::{}", compname.to_string(), libname.to_string()), flags, + env: None, + args: vec![], } } + /// Append args to this compartment. + pub fn args(&mut self, args: impl IntoIterator) -> &mut Self { + for arg in args.into_iter() { + self.args.push(arg.to_string()) + } + self + } + + /// Set the environment for the compartment + pub fn env(&mut self, env: Vec) -> &mut Self { + self.env = Some(env); + self + } + /// Load the compartment. pub fn load(&self) -> Result { - let len = lazy_sb::write_bytes_to_sb(self.name.as_bytes()); - let desc = gates::monitor_rt_load_compartment(len as u64, self.flags.bits()) - .ok() - .ok_or(gates::LoadCompartmentError::Unknown) - .flatten()?; + fn get_current_env() -> Vec { + std::env::vars() + .map(|(var, val)| format!("{}={}", var, val)) + .collect() + } + let name_len = self.name.as_bytes().len(); + let args_len = self + .args + .iter() + .fold(0, |acc, arg| acc + arg.as_bytes().len() + 1); + let env = self.env.clone().unwrap_or_else(|| get_current_env()); + let envs_len = env + .iter() + .fold(0, |acc, arg| acc + arg.as_bytes().len() + 1); + let mut bytes = self.name.as_bytes().to_vec(); + for arg in &self.args { + bytes.extend_from_slice(arg.as_bytes()); + bytes.push(0); + } + for env in env { + bytes.extend_from_slice(env.as_bytes()); + bytes.push(0); + } + let len = lazy_sb::write_bytes_to_sb(&bytes); + if len < envs_len + args_len + name_len { + return Err(gates::LoadCompartmentError::Unknown); + } + let desc = gates::monitor_rt_load_compartment( + name_len as u64, + args_len as u64, + envs_len as u64, + self.flags.bits(), + ) + .ok() + .ok_or(gates::LoadCompartmentError::Unknown) + .flatten()?; Ok(CompartmentHandle { desc: Some(desc) }) } } @@ -387,6 +437,8 @@ pub struct CompartmentInfo<'a> { pub sctx: ObjID, /// The compartment flags and status. pub flags: CompartmentFlags, + /// Number of libraries + pub nr_libs: usize, _pd: PhantomData<&'a ()>, } @@ -397,6 +449,7 @@ impl<'a> CompartmentInfo<'a> { id: raw.id, sctx: raw.sctx, flags: CompartmentFlags::from_bits_truncate(raw.flags), + nr_libs: raw.nr_libs, _pd: PhantomData, } } @@ -422,6 +475,14 @@ impl CompartmentHandle { pub fn libs(&self) -> LibraryIter<'_> { LibraryIter::new(self) } + + pub fn wait(&self, flags: CompartmentFlags) -> CompartmentFlags { + CompartmentFlags::from_bits_truncate( + gates::monitor_rt_compartment_wait(self.desc(), flags.bits()) + .ok() + .unwrap_or(0), + ) + } } /// An iterator over libraries in a compartment. @@ -481,10 +542,18 @@ bitflags::bitflags! { /// Compartment state flags. #[derive(Clone, Debug, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)] pub struct CompartmentFlags : u64 { - /// Compartment is ready (libraries relocated and constructors run). + /// Compartment is ready (loaded, reloacated, runtime started and ctors run). const READY = 0x1; - /// The main thread has exited. - const EXITED = 0x2; + /// Compartment is a binary, not a library. + const IS_BINARY = 0x2; + /// Compartment runtime thread may exit. + const THREAD_CAN_EXIT = 0x4; + /// Compartment thread has been started once. + const STARTED = 0x8; + /// Compartment destructors have run. + const DESTRUCTED = 0x10; + /// Compartment thread has exited. + const EXITED = 0x20; } } @@ -577,3 +646,72 @@ mod lazy_sb { LAZY_SB.borrow_mut().write(buf) } } + +pub const THREAD_STARTED: u32 = 1; +pub struct RuntimeThreadControl { + // Need to keep a lock for the ID, though we don't expect to use it much. + pub internal_lock: AtomicU32, + pub flags: AtomicU32, + pub id: UnsafeCell, +} + +impl Default for RuntimeThreadControl { + fn default() -> Self { + Self::new(0) + } +} + +impl RuntimeThreadControl { + pub const fn new(id: u32) -> Self { + Self { + internal_lock: AtomicU32::new(0), + flags: AtomicU32::new(0), + id: UnsafeCell::new(id), + } + } + + fn write_lock(&self) { + loop { + let old = self.internal_lock.fetch_or(1, Ordering::Acquire); + if old == 0 { + break; + } + } + } + + fn write_unlock(&self) { + self.internal_lock.fetch_and(!1, Ordering::Release); + } + + fn read_lock(&self) { + loop { + let old = self.internal_lock.fetch_add(2, Ordering::Acquire); + // If this happens, something has gone very wrong. + if old > i32::MAX as u32 { + twizzler_rt_abi::core::twz_rt_abort(); + } + if old & 1 == 0 { + break; + } + } + } + + fn read_unlock(&self) { + self.internal_lock.fetch_sub(2, Ordering::Release); + } + + pub fn set_id(&self, id: u32) { + self.write_lock(); + unsafe { + *self.id.get().as_mut().unwrap() = id; + } + self.write_unlock(); + } + + pub fn id(&self) -> u32 { + self.read_lock(); + let id = unsafe { *self.id.get().as_ref().unwrap() }; + self.read_unlock(); + id + } +} diff --git a/src/runtime/monitor/Cargo.toml b/src/runtime/monitor/Cargo.toml index 07be5302..3c7c7411 100644 --- a/src/runtime/monitor/Cargo.toml +++ b/src/runtime/monitor/Cargo.toml @@ -3,18 +3,15 @@ name = "monitor" version = "0.1.0" edition = "2021" -[lib] -crate-type = ["dylib"] - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] twizzler-abi = { path = "../../lib/twizzler-abi", default-features = false } twizzler-rt-abi = { path = "../../abi/rt-abi" } dynlink = { path = "../dynlink" } -tracing = "0.1" -tracing-subscriber = "0.3" -twz-rt = { path = "../rt", features = ["runtime"] } +tracing = { version = "0.1", pub = false } +tracing-subscriber = { version = "0.3", pub = false } +twizzler-runtime = { path = "../rt" } twizzler-object = { path = "../../lib/twizzler-object" } miette = "5.10" secgate = { path = "../secgate" } diff --git a/src/runtime/monitor/secapi/gates.rs b/src/runtime/monitor/secapi/gates.rs index 7ff01be5..04da1652 100644 --- a/src/runtime/monitor/secapi/gates.rs +++ b/src/runtime/monitor/secapi/gates.rs @@ -8,6 +8,10 @@ use twizzler_rt_abi::{ thread::{SpawnError, ThreadSpawnArgs}, }; +extern "C-unwind" { + fn __is_monitor_ready() -> bool; +} + /// Reserved instance ID for the security monitor. pub const MONITOR_INSTANCE_ID: ObjID = ObjID::new(0); @@ -94,6 +98,7 @@ pub struct CompartmentInfo { pub id: ObjID, pub sctx: ObjID, pub flags: u64, + pub nr_libs: usize, } #[cfg_attr(feature = "secgate-impl", secgate::secure_gate(options(info)))] @@ -156,6 +161,8 @@ unsafe impl Crossing for LibraryInfo {} pub fn monitor_rt_load_compartment( info: &secgate::GateCallInfo, name_len: u64, + args_len: u64, + env_len: u64, flags: u32, ) -> Result { let monitor = crate::mon::get_monitor(); @@ -164,6 +171,8 @@ pub fn monitor_rt_load_compartment( caller, info.thread_id(), name_len as usize, + args_len as usize, + env_len as usize, NewCompartmentFlags::from_bits(flags).ok_or(LoadCompartmentError::Unknown)?, ) } @@ -181,6 +190,21 @@ impl Display for LoadCompartmentError { } } +#[cfg_attr(feature = "secgate-impl", secgate::secure_gate(options(info)))] +#[cfg_attr( + not(feature = "secgate-impl"), + secgate::secure_gate(options(info, api)) +)] +pub fn monitor_rt_compartment_wait( + info: &secgate::GateCallInfo, + desc: Option, + flags: u64, +) -> u64 { + let monitor = crate::mon::get_monitor(); + let caller = info.source_context().unwrap_or(MONITOR_INSTANCE_ID); + monitor.compartment_wait(caller, desc, flags) +} + #[cfg_attr(feature = "secgate-impl", secgate::secure_gate(options(info)))] #[cfg_attr( not(feature = "secgate-impl"), @@ -234,10 +258,8 @@ pub fn monitor_rt_object_map( id: ObjID, flags: twizzler_rt_abi::object::MapFlags, ) -> Result { - use twz_rt::{RuntimeState, OUR_RUNTIME}; - use crate::mon::space::MapInfo; - if OUR_RUNTIME.state().contains(RuntimeState::READY) { + if unsafe { __is_monitor_ready() } { // Are we recursing from the monitor, with a lock held? In that case, use early_object_map // to map the object. This will leak this mapping, but this is both rare, and then // since the mapping is leaked, it can be used as an allocator object indefinitely @@ -277,8 +299,7 @@ pub fn monitor_rt_object_unmap( id: ObjID, flags: twizzler_rt_abi::object::MapFlags, ) { - use twz_rt::{RuntimeState, OUR_RUNTIME}; - if OUR_RUNTIME.state().contains(RuntimeState::READY) { + if unsafe { __is_monitor_ready() } { let monitor = crate::mon::get_monitor(); monitor.unmap_object( info.source_context().unwrap_or(MONITOR_INSTANCE_ID), diff --git a/src/runtime/monitor/src/init.rs b/src/runtime/monitor/src/init.rs index 29931735..0211d466 100644 --- a/src/runtime/monitor/src/init.rs +++ b/src/runtime/monitor/src/init.rs @@ -1,8 +1,6 @@ -use dynlink::context::{runtime::RuntimeInitInfo, Context}; -use twizzler_rt_abi::core::{RuntimeInfo, RUNTIME_INIT_MONITOR}; -use twz_rt::{preinit::preinit_abort, preinit_println}; +use std::ffi::c_void; -static mut RTINFO: Option<*const RuntimeInitInfo> = None; +use dynlink::context::{runtime::RuntimeInitInfo, Context}; pub(crate) struct InitDynlinkContext { pub ctx: *mut Context, @@ -20,46 +18,14 @@ impl InitDynlinkContext { } } +extern "C-unwind" { + // Defined by the runtime. Returns a pointer to the runtime init info struct if the runtime is + // in monitor mode, otherwise returns null. + fn __is_monitor() -> *mut c_void; +} pub(crate) fn bootstrap_dynlink_context() -> Option { - let info = unsafe { RTINFO.unwrap().as_ref().unwrap() }; + let info = unsafe { __is_monitor().cast::().as_mut().unwrap() }; let ctx = info.ctx as *mut Context; Some(InitDynlinkContext { ctx }) } - -#[no_mangle] -pub unsafe extern "C" fn monitor_entry_from_bootstrap(rtinfo_ptr: *const RuntimeInfo) { - let rtinfo = unsafe { rtinfo_ptr.as_ref().unwrap() }; - if rtinfo.kind != RUNTIME_INIT_MONITOR { - preinit_println!("cannot initialize monitor without monitor runtime init info"); - preinit_abort(); - } - let rt_init_info_ptr = rtinfo.init_info.monitor.cast(); - - unsafe { - RTINFO = Some(rt_init_info_ptr); - twizzler_rt_abi::core::rt0::rust_entry(rtinfo_ptr) - } -} - -#[allow(improper_ctypes)] -extern "C" { - fn twizzler_call_lang_start( - main: fn(), - argc: isize, - argv: *const *const u8, - sigpipe: u8, - ) -> isize; -} - -#[cfg(not(test))] -#[no_mangle] -pub extern "C" fn main(argc: i32, argv: *const *const u8) -> i32 { - //TODO: sigpipe? - unsafe { twizzler_call_lang_start(crate::main, argc as isize, argv, 0) as i32 } -} - -// TODO: we should probably get this for real. -#[cfg(not(test))] -#[no_mangle] -pub extern "C" fn _init() {} diff --git a/src/runtime/monitor/src/lib.rs b/src/runtime/monitor/src/lib.rs deleted file mode 100644 index 2ca6bcec..00000000 --- a/src/runtime/monitor/src/lib.rs +++ /dev/null @@ -1,64 +0,0 @@ -#![feature(naked_functions)] -#![feature(thread_local)] -#![feature(hash_extract_if)] -#![feature(new_zeroed_alloc)] -#![feature(iterator_try_collect)] - -use tracing::{debug, info, warn, Level}; -use tracing_subscriber::{fmt::format::FmtSpan, FmtSubscriber}; -use twz_rt::{set_upcall_handler, OUR_RUNTIME}; - -mod dlengine; -mod init; -mod mon; -mod upcall; - -pub use monitor_api::MappedObjectAddrs; - -#[path = "../secapi/gates.rs"] -mod gates; - -pub fn main() { - // For early init, if something breaks, we really want to see everything... - std::env::set_var("RUST_BACKTRACE", "full"); - let subscriber = FmtSubscriber::builder() - .with_max_level(Level::DEBUG) - .with_target(false) - .with_span_events(FmtSpan::ACTIVE) - .finish(); - - tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); - - miette::set_hook(Box::new(|_| { - Box::new(miette::NarratableReportHandler::new().with_cause_chain()) - })) - .unwrap(); - - debug!("monitor entered, discovering dynlink context"); - let init = - init::bootstrap_dynlink_context().expect("failed to discover initial dynlink context"); - - let mon = mon::Monitor::new(init); - mon::set_monitor(mon); - - debug!("ok, starting monitor proper"); - // Safety: the monitor is ready, and so we can set our runtime as ready to use the monitor. - unsafe { OUR_RUNTIME.set_runtime_ready() }; - // Had to wait till now to be able to spawn threads. - mon::get_monitor().start_background_threads(); - - std::env::set_var("RUST_BACKTRACE", "1"); - set_upcall_handler(&crate::upcall::upcall_monitor_handler).unwrap(); - - let main_thread = std::thread::spawn(monitor_init); - let _r = main_thread.join().unwrap().map_err(|e| { - tracing::error!("{:?}", e); - }); - warn!("monitor main thread exited"); -} - -fn monitor_init() -> miette::Result<()> { - info!("monitor early init completed, starting init"); - - Ok(()) -} diff --git a/src/runtime/monitor/src/main.rs b/src/runtime/monitor/src/main.rs new file mode 100644 index 00000000..4ebccf10 --- /dev/null +++ b/src/runtime/monitor/src/main.rs @@ -0,0 +1,120 @@ +#![feature(naked_functions)] +#![feature(thread_local)] +#![feature(hash_extract_if)] +#![feature(new_zeroed_alloc)] +#![feature(iterator_try_collect)] + +use std::time::Duration; + +use dynlink::context::NewCompartmentFlags; +use miette::IntoDiagnostic; +use monitor_api::{CompartmentFlags, CompartmentHandle, CompartmentLoader}; +use tracing::{debug, info, warn, Level}; +use tracing_subscriber::{fmt::format::FmtSpan, FmtSubscriber}; +use twizzler_abi::object::NULLPAGE_SIZE; +use twizzler_rt_abi::object::MapFlags; + +mod dlengine; +pub mod init; +mod mon; +mod upcall; + +pub use monitor_api::MappedObjectAddrs; + +#[path = "../secapi/gates.rs"] +mod gates; + +extern crate dynlink; +extern crate twizzler_runtime; + +extern "C-unwind" { + fn __monitor_ready(); +} + +pub fn main() { + // For early init, if something breaks, we really want to see everything... + std::env::set_var("RUST_BACKTRACE", "full"); + let subscriber = FmtSubscriber::builder() + .with_max_level(Level::INFO) + .with_target(false) + .with_span_events(FmtSpan::ACTIVE) + .finish(); + + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + + miette::set_hook(Box::new(|_| { + Box::new(miette::NarratableReportHandler::new().with_cause_chain()) + })) + .unwrap(); + + debug!("monitor entered, discovering dynlink context"); + let init = + init::bootstrap_dynlink_context().expect("failed to discover initial dynlink context"); + + let mon = mon::Monitor::new(init); + mon::set_monitor(mon); + + debug!("ok, starting monitor proper"); + // Safety: the monitor is ready, and so we can set our runtime as ready to use the monitor. + unsafe { __monitor_ready() }; + // Had to wait till now to be able to spawn threads. + mon::get_monitor().start_background_threads(); + + std::env::set_var("RUST_BACKTRACE", "1"); + unsafe { + twizzler_rt_abi::bindings::twz_rt_set_upcall_handler(Some( + crate::upcall::upcall_monitor_handler_entry, + )) + }; + + let main_thread = std::thread::spawn(monitor_init); + let _r = main_thread.join().unwrap().map_err(|e| { + tracing::error!("{:?}", e); + }); + warn!("monitor main thread exited"); +} + +fn monitor_init() -> miette::Result<()> { + // If we have monitor tests to run, do so. + if let Some(ki_name) = dlengine::get_kernel_init_info() + .names() + .iter() + .find(|iname| iname.name() == "monitor_test_info") + { + info!("starting monitor tests [{}]", ki_name.name()); + // Read the monitor test binary name. + const MAX_NAMELEN: usize = 0x1000; + let info = + twizzler_rt_abi::object::twz_rt_map_object(ki_name.id(), MapFlags::READ).unwrap(); + let test_name_slice = + unsafe { core::slice::from_raw_parts(info.start().add(NULLPAGE_SIZE), MAX_NAMELEN) }; + let first_null = test_name_slice + .iter() + .position(|x| *x == 0) + .unwrap_or(MAX_NAMELEN - 1); + let test_name = String::from_utf8_lossy(&test_name_slice[0..first_null]); + debug!("monitor test binary: {}", test_name); + if let Some(_ki_name) = dlengine::get_kernel_init_info() + .names() + .iter() + .find(|iname| iname.name() == test_name) + { + // Load and wait for tests to complete + let comp: CompartmentHandle = + CompartmentLoader::new("montest", test_name, NewCompartmentFlags::empty()) + .args(&["montest"]) + .load() + .into_diagnostic()?; + let mut flags = comp.info().flags; + while !flags.contains(CompartmentFlags::EXITED) { + flags = comp.wait(flags); + } + } else { + tracing::error!("failed to start monitor tests: {}", ki_name.name()); + } + } + info!("monitor early init completed, starting init"); + // TODO: start init... + + Ok(()) +} diff --git a/src/runtime/monitor/src/mon/compartment.rs b/src/runtime/monitor/src/mon/compartment.rs index e67ac7bc..bdba4986 100644 --- a/src/runtime/monitor/src/mon/compartment.rs +++ b/src/runtime/monitor/src/mon/compartment.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, ffi::CStr}; use dynlink::{ compartment::CompartmentId, @@ -203,7 +203,7 @@ impl super::Monitor { thread: ObjID, desc: Option, ) -> Option { - let (ref mut space, _, ref mut comps, _, _, ref comphandles) = + let (ref mut space, _, ref mut comps, ref dynlink, _, ref comphandles) = *self.locks.lock(ThreadKey::get().unwrap()); let comp_id = desc .map(|comp| comphandles.lookup(instance, comp).map(|ch| ch.instance)) @@ -213,12 +213,18 @@ impl super::Monitor { let pt = comps.get_mut(instance)?.get_per_thread(thread, space); let name_len = pt.write_bytes(name.as_bytes()); let comp = comps.get(comp_id)?; + let nr_libs = dynlink + .get_compartment(comp.compartment_id) + .ok()? + .library_ids() + .count(); Some(CompartmentInfo { name_len, id: comp_id, sctx: comp.sctx, flags: comp.raw_flags(), + nr_libs, }) } @@ -239,14 +245,37 @@ impl super::Monitor { ) } + pub fn compartment_wait(&self, caller: ObjID, desc: Option, flags: u64) -> u64 { + let Some(instance) = ({ + let comphandles = self._compartment_handles.write(ThreadKey::get().unwrap()); + let comp_id = desc + .map(|comp| comphandles.lookup(caller, comp).map(|ch| ch.instance)) + .unwrap_or(Some(caller)); + comp_id + }) else { + return 0; + }; + self.wait_for_compartment_state_change(instance, flags); + self.load_compartment_flags(instance) + } + /// Open a handle to the n'th dependency compartment of a given compartment. pub fn get_compartment_deps( &self, - _caller: ObjID, - _desc: Option, - _dep_n: usize, + caller: ObjID, + desc: Option, + dep_n: usize, ) -> Option { - todo!() + let dep = { + let (_, _, ref mut comps, _, _, ref mut comphandles) = + *self.locks.lock(ThreadKey::get().unwrap()); + let comp_id = desc + .map(|comp| comphandles.lookup(caller, comp).map(|ch| ch.instance)) + .unwrap_or(Some(caller))?; + let comp = comps.get_mut(comp_id)?; + comp.deps.get(dep_n).cloned() + }?; + self.get_compartment_handle(caller, dep) } /// Load a new compartment with a root library ID, and return a compartment handle. @@ -255,17 +284,40 @@ impl super::Monitor { caller: ObjID, thread: ObjID, name_len: usize, + args_len: usize, + env_len: usize, new_comp_flags: NewCompartmentFlags, ) -> Result { - let name_bytes = self - .read_thread_simple_buffer(caller, thread, name_len) + let total_bytes = name_len + args_len + env_len; + let str_bytes = self + .read_thread_simple_buffer(caller, thread, total_bytes) .ok_or(LoadCompartmentError::Unknown)?; + let name_bytes = &str_bytes[0..name_len]; + let arg_bytes = &str_bytes[name_len..(name_len + args_len)]; + let env_bytes = &str_bytes[(name_len + args_len)..total_bytes]; + let input = String::from_utf8_lossy(&name_bytes); let mut split = input.split("::"); let compname = split.next().ok_or(LoadCompartmentError::Unknown)?; let libname = split.next().ok_or(LoadCompartmentError::Unknown)?; let root = UnloadedLibrary::new(libname); + // parse args + let args_bytes = arg_bytes.split_inclusive(|b| *b == 0); + let args = args_bytes + .map(CStr::from_bytes_with_nul) + .try_collect::>() + .map_err(|_| LoadCompartmentError::Unknown)?; + tracing::debug!("load {}: args: {:?}", compname, args); + + // parse env + let envs_bytes = env_bytes.split_inclusive(|b| *b == 0); + let env = envs_bytes + .map(CStr::from_bytes_with_nul) + .try_collect::>() + .map_err(|_| LoadCompartmentError::Unknown)?; + tracing::trace!("load {}: env: {:?}", compname, env); + let loader = { let mut dynlink = self.dynlink.write(ThreadKey::get().unwrap()); loader::RunCompLoader::new(*dynlink, compname, root, new_comp_flags) @@ -293,7 +345,7 @@ impl super::Monitor { .get_compartment_handle(caller, root_comp) .ok_or(LoadCompartmentError::Unknown)?; - self.start_compartment(root_comp)?; + self.start_compartment(root_comp, &args, &env)?; Ok(desc) } diff --git a/src/runtime/monitor/src/mon/compartment/loader.rs b/src/runtime/monitor/src/mon/compartment/loader.rs index 39bcd181..0c1746d7 100644 --- a/src/runtime/monitor/src/mon/compartment/loader.rs +++ b/src/runtime/monitor/src/mon/compartment/loader.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, ptr::null_mut}; +use std::{collections::HashSet, ffi::CStr, ptr::null_mut}; use dynlink::{ compartment::CompartmentId, @@ -298,7 +298,12 @@ impl RunCompLoader { } impl Monitor { - pub(crate) fn start_compartment(&self, instance: ObjID) -> Result<(), LoadCompartmentError> { + pub(crate) fn start_compartment( + &self, + instance: ObjID, + args: &[&CStr], + env: &[&CStr], + ) -> Result<(), LoadCompartmentError> { let deps = { let cmp = self.comp_mgr.read(ThreadKey::get().unwrap()); let rc = cmp.get(instance).ok_or(LoadCompartmentError::Unknown)?; @@ -310,7 +315,7 @@ impl Monitor { rc.deps.clone() }; for dep in deps { - self.start_compartment(dep)?; + self.start_compartment(dep, &[], env)?; } { @@ -337,7 +342,7 @@ impl Monitor { *self.locks.lock(ThreadKey::get().unwrap()); let rc = cmp.get_mut(instance).ok_or(LoadCompartmentError::Unknown)?; - rc.start_main_thread(state, &mut *space, &mut *tmgr, &mut *dynlink) + rc.start_main_thread(state, &mut *space, &mut *tmgr, &mut *dynlink, args, env) }; if info.is_none() { diff --git a/src/runtime/monitor/src/mon/compartment/runcomp.rs b/src/runtime/monitor/src/mon/compartment/runcomp.rs index a7737069..39d590b5 100644 --- a/src/runtime/monitor/src/mon/compartment/runcomp.rs +++ b/src/runtime/monitor/src/mon/compartment/runcomp.rs @@ -1,12 +1,13 @@ use std::{ alloc::Layout, collections::HashMap, + ffi::{CStr, CString}, ptr::NonNull, sync::atomic::{AtomicU64, Ordering}, }; use dynlink::{compartment::CompartmentId, context::Context}; -use monitor_api::{SharedCompConfig, TlsTemplateInfo}; +use monitor_api::{CompartmentFlags, RuntimeThreadControl, SharedCompConfig, TlsTemplateInfo}; use secgate::util::SimpleBuffer; use talc::{ErrOnOom, Talc}; use twizzler_abi::syscall::{ @@ -16,7 +17,6 @@ use twizzler_rt_abi::{ core::{CompartmentInitInfo, CtorSet, InitInfoPtrs, RuntimeInfo, RUNTIME_INIT_COMP}, object::{MapError, MapFlags, ObjID}, }; -use twz_rt::RuntimeThreadControl; use super::{compconfig::CompConfigObject, compthread::CompThread, StackObject}; use crate::mon::{ @@ -25,17 +25,17 @@ use crate::mon::{ }; /// Compartment is ready (loaded, reloacated, runtime started and ctors run). -pub const COMP_READY: u64 = 0x1; +pub const COMP_READY: u64 = CompartmentFlags::READY.bits(); /// Compartment is a binary, not a library. -pub const COMP_IS_BINARY: u64 = 0x2; +pub const COMP_IS_BINARY: u64 = CompartmentFlags::IS_BINARY.bits(); /// Compartment runtime thread may exit. -pub const COMP_THREAD_CAN_EXIT: u64 = 0x4; +pub const COMP_THREAD_CAN_EXIT: u64 = CompartmentFlags::THREAD_CAN_EXIT.bits(); /// Compartment thread has been started once. -pub const COMP_STARTED: u64 = 0x8; +pub const COMP_STARTED: u64 = CompartmentFlags::STARTED.bits(); /// Compartment destructors have run. -pub const COMP_DESTRUCTED: u64 = 0x10; +pub const COMP_DESTRUCTED: u64 = CompartmentFlags::DESTRUCTED.bits(); /// Compartment thread has exited. -pub const COMP_EXITED: u64 = 0x20; +pub const COMP_EXITED: u64 = CompartmentFlags::EXITED.bits(); /// A runnable or running compartment. pub struct RunComp { @@ -60,7 +60,7 @@ pub struct RunComp { impl Drop for RunComp { fn drop(&mut self) { - //tracing::warn!("todo: runcomp drop"); + // TODO: check if we need to do anything. } } @@ -188,7 +188,7 @@ impl RunComp { } /// Allocate some space in the compartment allocator, and initialize it. - pub fn monitor_new(&mut self, data: T) -> Result<*mut T, ()> { + pub fn monitor_new(&mut self, data: T) -> Result<*mut T, ()> { unsafe { let place: NonNull = self.alloc.malloc(Layout::new::())?.cast(); place.as_ptr().write(data); @@ -197,10 +197,7 @@ impl RunComp { } /// Allocate some space in the compartment allocator for a slice, and initialize it. - pub fn monitor_new_slice( - &mut self, - data: &[T], - ) -> Result<*mut T, ()> { + pub fn monitor_new_slice(&mut self, data: &[T]) -> Result<*mut T, ()> { unsafe { let place = self.alloc.malloc(Layout::array::(data.len()).unwrap())?; let slice = core::slice::from_raw_parts_mut(place.as_ptr() as *mut T, data.len()); @@ -265,6 +262,8 @@ impl RunComp { space: &mut Space, tmgr: &mut ThreadMgr, dynlink: &mut Context, + args: &[&CStr], + env: &[&CStr], ) -> Option { if self.has_flag(COMP_STARTED) { return Some(false); @@ -292,6 +291,29 @@ impl RunComp { self.comp_config_object.get_comp_config() as *mut SharedCompConfig; let ctors_in_comp = self.monitor_new_slice(&ctors).ok()?; + // TODO: unwrap + let mut args_in_comp: Vec<_> = args + .iter() + .map(|arg| self.monitor_new_slice(arg.to_bytes_with_nul()).unwrap()) + .collect(); + + if args_in_comp.len() == 0 { + let cname = CString::new(self.name.as_bytes()).unwrap(); + args_in_comp = vec![self.monitor_new_slice(cname.as_bytes()).unwrap()]; + } + let argc = args_in_comp.len(); + + let mut envs_in_comp: Vec<_> = env + .iter() + .map(|arg| self.monitor_new_slice(arg.to_bytes_with_nul()).unwrap()) + .collect(); + + args_in_comp.push(core::ptr::null_mut()); + envs_in_comp.push(core::ptr::null_mut()); + + let args_in_comp_in_comp = self.monitor_new_slice(&args_in_comp).unwrap(); + let envs_in_comp_in_comp = self.monitor_new_slice(&envs_in_comp).unwrap(); + let comp_init_info = CompartmentInitInfo { ctor_set_array: ctors_in_comp, ctor_set_len: ctors.len(), @@ -302,9 +324,9 @@ impl RunComp { let rtinfo = RuntimeInfo { flags: 0, kind: RUNTIME_INIT_COMP, - args: core::ptr::null_mut(), - argc: 0, - envp: core::ptr::null_mut(), + args: args_in_comp_in_comp.cast(), + argc, + envp: envs_in_comp_in_comp.cast(), init_info: InitInfoPtrs { comp: comp_init_info_in_comp, }, diff --git a/src/runtime/monitor/src/mon/library.rs b/src/runtime/monitor/src/mon/library.rs index 574fc037..aa675216 100644 --- a/src/runtime/monitor/src/mon/library.rs +++ b/src/runtime/monitor/src/mon/library.rs @@ -65,7 +65,7 @@ impl Monitor { let rc = comps.get(comp_id)?; let dcomp = dynlink.get_compartment(rc.compartment_id).ok()?; let id = dcomp.library_ids().nth(num)?; - handles.insert(comp_id, LibraryHandle { comp: comp_id, id }) + handles.insert(caller, LibraryHandle { comp: comp_id, id }) } /// Load a library in the given compartment. @@ -80,6 +80,7 @@ impl Monitor { /// Drop a library handle. pub fn drop_library_handle(&self, caller: ObjID, desc: Descriptor) { + //tracing::info!("drop: {}", desc); self.library_handles .write(ThreadKey::get().unwrap()) .remove(caller, desc); diff --git a/src/runtime/monitor/src/mon/mod.rs b/src/runtime/monitor/src/mon/mod.rs index 13f2e8e6..a20946cc 100644 --- a/src/runtime/monitor/src/mon/mod.rs +++ b/src/runtime/monitor/src/mon/mod.rs @@ -6,7 +6,7 @@ use compartment::{ }; use dynlink::compartment::MONITOR_COMPARTMENT_ID; use happylock::{LockCollection, RwLock, ThreadKey}; -use monitor_api::{SharedCompConfig, TlsTemplateInfo, MONITOR_INSTANCE_ID}; +use monitor_api::{RuntimeThreadControl, SharedCompConfig, TlsTemplateInfo, MONITOR_INSTANCE_ID}; use secgate::util::HandleMgr; use thread::DEFAULT_STACK_SIZE; use twizzler_abi::{syscall::sys_thread_exit, upcall::UpcallFrame}; @@ -14,7 +14,6 @@ use twizzler_rt_abi::{ object::{MapError, MapFlags, ObjID}, thread::{SpawnError, ThreadSpawnArgs}, }; -use twz_rt::{RuntimeState, RuntimeThreadControl, OUR_RUNTIME}; use self::{ compartment::{CompConfigObject, CompartmentHandle, RunComp}, @@ -68,7 +67,6 @@ impl Monitor { /// Start the background threads for the monitor instance. Must be done only once the monitor /// has been initialized. pub fn start_background_threads(&self) { - assert!(OUR_RUNTIME.state().contains(RuntimeState::READY)); let cleaner = ThreadCleaner::new(); self.unmapper.set(Unmapper::new()).ok().unwrap(); self.thread_mgr @@ -260,7 +258,7 @@ impl Monitor { cmd: MonitorCompControlCmd, ) -> Option { let src = info.source_context()?; - tracing::trace!( + tracing::debug!( "compartment ctrl from: {:?}, thread = {:?}: {:?}", src, info.thread_id(), diff --git a/src/runtime/monitor/src/mon/space.rs b/src/runtime/monitor/src/mon/space.rs index dcbb6cba..0b03d042 100644 --- a/src/runtime/monitor/src/mon/space.rs +++ b/src/runtime/monitor/src/mon/space.rs @@ -51,6 +51,13 @@ fn mapflags_into_prot(flags: MapFlags) -> Protections { prot } +extern "C-unwind" { + fn __monitor_get_slot() -> isize; + fn __monitor_get_pair(one: *mut usize, two: *mut usize) -> bool; + fn __monitor_release_pair(one: usize, two: usize); + fn __monitor_release_slot(slot: usize); +} + impl Space { /// Get the stats. pub fn stat(&self) -> SpaceStats { @@ -66,8 +73,9 @@ impl Space { Some(item) => item, None => { // Not yet mapped, so allocate a slot and map it. - let slot = twz_rt::OUR_RUNTIME - .allocate_slot() + let slot = unsafe { __monitor_get_slot() } + .try_into() + .ok() .ok_or(MapError::OutOfResources)?; let Ok(_) = sys_object_map( @@ -77,7 +85,9 @@ impl Space { mapflags_into_prot(info.flags), twizzler_abi::syscall::MapFlags::empty(), ) else { - twz_rt::OUR_RUNTIME.release_slot(slot); + unsafe { + __monitor_release_slot(slot); + } return Err(MapError::Other); }; @@ -173,10 +183,14 @@ pub(crate) struct UnmapOnDrop { impl Drop for UnmapOnDrop { fn drop(&mut self) { - if sys_object_unmap(None, self.slot, UnmapFlags::empty()).is_ok() { - twz_rt::OUR_RUNTIME.release_slot(self.slot); - } else { - tracing::warn!("failed to unmap slot {}", self.slot); + match sys_object_unmap(None, self.slot, UnmapFlags::empty()) { + Ok(_) => unsafe { + __monitor_release_slot(self.slot); + }, + Err(_e) => { + // TODO: once the kernel-side works properly, uncomment this. + //tracing::warn!("failed to unmap slot {}: {}", self.slot, e); + } } } } @@ -184,7 +198,7 @@ impl Drop for UnmapOnDrop { /// Map an object into the address space, without tracking it. This leaks the mapping, but is useful /// for bootstrapping. See the object mapping gate comments for more details. pub fn early_object_map(info: MapInfo) -> MappedObjectAddrs { - let slot = twz_rt::OUR_RUNTIME.allocate_slot().unwrap(); + let slot = unsafe { __monitor_get_slot() }.try_into().unwrap(); sys_object_map( None, diff --git a/src/runtime/monitor/src/mon/thread.rs b/src/runtime/monitor/src/mon/thread.rs index 9147225c..2ca98724 100644 --- a/src/runtime/monitor/src/mon/thread.rs +++ b/src/runtime/monitor/src/mon/thread.rs @@ -6,7 +6,7 @@ use std::{ }; use dynlink::{compartment::Compartment, tls::TlsRegion}; -use monitor_api::MONITOR_INSTANCE_ID; +use monitor_api::{RuntimeThreadControl, MONITOR_INSTANCE_ID}; use twizzler_abi::{ object::NULLPAGE_SIZE, syscall::{sys_spawn, sys_thread_exit, ThreadSyncSleep, UpcallTargetSpawnOption}, @@ -17,7 +17,6 @@ use twizzler_rt_abi::{ object::{MapFlags, ObjID}, thread::SpawnError, }; -use twz_rt::RuntimeThreadControl; use super::space::{MapHandle, MapInfo, Space}; use crate::gates::ThreadMgrStats; @@ -75,7 +74,7 @@ impl ThreadMgr { ) -> Result { let upcall_target = UpcallTarget::new( None, - Some(twz_rt::rr_upcall_entry), + Some(twizzler_rt_abi::arch::__twz_rt_upcall_entry), super_stack_start, SUPER_UPCALL_STACK_SIZE, super_thread_pointer, diff --git a/src/runtime/monitor/src/mon/thread/cleaner.rs b/src/runtime/monitor/src/mon/thread/cleaner.rs index 1d060455..8b3f5fca 100644 --- a/src/runtime/monitor/src/mon/thread/cleaner.rs +++ b/src/runtime/monitor/src/mon/thread/cleaner.rs @@ -105,7 +105,6 @@ impl Waits { } } -#[tracing::instrument(skip(data, recv))] fn cleaner_thread_main(data: Pin>, mut recv: Receiver) { // TODO (dbittman): when we have support for async thread events, we can use that API. let mut ops = Vec::new(); diff --git a/src/runtime/monitor/src/upcall.rs b/src/runtime/monitor/src/upcall.rs index 22d9b039..6dc89026 100644 --- a/src/runtime/monitor/src/upcall.rs +++ b/src/runtime/monitor/src/upcall.rs @@ -1,8 +1,10 @@ -use std::sync::atomic::{AtomicBool, Ordering}; +use std::{ + ffi::c_void, + sync::atomic::{AtomicBool, Ordering}, +}; use tracing::info; use twizzler_abi::upcall::{UpcallData, UpcallFrame, UpcallHandlerFlags}; -use twz_rt::preinit_println; #[thread_local] static IN_UPCALL_HANDLER: AtomicBool = AtomicBool::new(false); @@ -15,7 +17,7 @@ pub fn upcall_monitor_handler(frame: &mut UpcallFrame, info: &UpcallData) { twizzler_abi::syscall::sys_thread_exit(101); } } else { - preinit_println!( + twizzler_abi::klog_println!( "monitor got unexpected upcall while in supervisor context: {:?} {:?}", frame, info @@ -27,3 +29,12 @@ pub fn upcall_monitor_handler(frame: &mut UpcallFrame, info: &UpcallData) { // TODO: we don't always need to exit. twizzler_abi::syscall::sys_thread_exit(101); } + +pub extern "C-unwind" fn upcall_monitor_handler_entry(frame: *mut c_void, info: *const c_void) { + unsafe { + upcall_monitor_handler( + frame.cast::().as_mut().unwrap(), + info.cast::().as_ref().unwrap(), + ); + } +} diff --git a/src/runtime/monitor/tests/montest-lib/Cargo.toml b/src/runtime/monitor/tests/montest-lib/Cargo.toml new file mode 100644 index 00000000..061d3a8f --- /dev/null +++ b/src/runtime/monitor/tests/montest-lib/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "montest-lib" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib"] + +[dependencies] +secgate = { path = "../../../secgate" } +twizzler-runtime = { path = "../../../rt" } +twizzler-abi = { path = "../../../../lib/twizzler-abi" } diff --git a/src/runtime/monitor/tests/montest-lib/src/lib.rs b/src/runtime/monitor/tests/montest-lib/src/lib.rs new file mode 100644 index 00000000..e99d8efb --- /dev/null +++ b/src/runtime/monitor/tests/montest-lib/src/lib.rs @@ -0,0 +1,53 @@ +#![feature(naked_functions)] +#![feature(thread_local)] + +extern crate twizzler_runtime; + +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; + +#[secgate::secure_gate] +pub fn test_thread_local_call_count() -> usize { + #[thread_local] + static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); + CALL_COUNT.fetch_add(1, Ordering::SeqCst) + 1 +} + +#[secgate::secure_gate] +pub fn test_global_call_count() -> usize { + static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); + CALL_COUNT.fetch_add(1, Ordering::SeqCst) + 1 +} + +#[secgate::secure_gate] +pub fn test_internal_panic(catch_it: bool) -> usize { + if catch_it { + let x = std::panic::catch_unwind(|| { + panic!("test_panic (to be caught)"); + }); + return if x.is_err() { 1 } else { 0 }; + } + panic!("test_panic (not caught)"); +} + +#[secgate::secure_gate] +pub fn test_was_ctor_run() -> bool { + WAS_CTOR_RUN.load(Ordering::SeqCst) +} + +static WAS_CTOR_RUN: AtomicBool = AtomicBool::new(false); + +#[used] +#[doc(hidden)] +#[allow(non_upper_case_globals)] +#[link_section = ".init_array"] +static ___cons_test___ctor: unsafe extern "C" fn() = { + #[allow(non_snake_case)] + #[link_section = ".text.startup"] + unsafe extern "C" fn ___cons_test___ctor() { + cons_test() + } + ___cons_test___ctor +}; +unsafe fn cons_test() { + WAS_CTOR_RUN.store(true, Ordering::SeqCst); +} diff --git a/src/runtime/monitor/tests/montest/Cargo.toml b/src/runtime/monitor/tests/montest/Cargo.toml new file mode 100644 index 00000000..1985f3d3 --- /dev/null +++ b/src/runtime/monitor/tests/montest/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "montest" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +twizzler-runtime = { path = "../../../rt" } +secgate = { path = "../../../secgate" } +montest-lib = { path = "../montest-lib" } +monitor = { path = "../../../monitor" } +ctor = "*" +tracing = "0.1" +tracing-subscriber = "0.3" +twizzler-abi = { path = "../../../../lib/twizzler-abi" } diff --git a/src/runtime/monitor/tests/montest/src/main.rs b/src/runtime/monitor/tests/montest/src/main.rs new file mode 100644 index 00000000..27c3018c --- /dev/null +++ b/src/runtime/monitor/tests/montest/src/main.rs @@ -0,0 +1,118 @@ +use std::sync::atomic::{AtomicBool, Ordering}; + +extern crate secgate; + +secgate::secgate_prelude!(); + +extern crate tracing; +extern crate tracing_subscriber; +extern crate twizzler_runtime; + +mod montest_lib { + #[link(name = "montest_lib")] + extern "C" {} + #[secgate::secure_gate(options(info, api))] + pub fn test_was_ctor_run(info: &GateCallInfo) -> bool {} + + #[secgate::secure_gate(options(info, api))] + pub fn test_internal_panic(info: &GateCallInfo, catch_it: bool) -> usize {} + + #[secgate::secure_gate(options(info, api))] + pub fn test_global_call_count(info: &GateCallInfo) -> usize {} + + #[secgate::secure_gate(options(info, api))] + pub fn test_thread_local_call_count(info: &GateCallInfo) -> usize {} +} + +fn main() { + setup_logging(); + montest_lib::test_global_call_count(); +} +use tracing::Level; +fn setup_logging() { + let _ = tracing::subscriber::set_global_default( + tracing_subscriber::fmt() + .with_max_level(Level::DEBUG) + .finish(), + ); +} + +#[cfg(test)] +mod tests { + use std::sync::atomic::Ordering; + + use crate::montest_lib; + extern crate secgate; + + use super::setup_logging; + use crate::WAS_CTOR_RUN; + + #[test] + fn test_tl_count() { + setup_logging(); + assert_eq!( + secgate::SecGateReturn::Success(1), + montest_lib::test_thread_local_call_count() + ); + } + + #[test] + fn test_gl_count() { + setup_logging(); + assert_eq!( + secgate::SecGateReturn::Success(1), + montest_lib::test_global_call_count() + ); + } + + #[test] + fn test_uncaught_internal_panic() { + setup_logging(); + assert_eq!( + secgate::SecGateReturn::CalleePanic, + montest_lib::test_internal_panic(false) + ); + } + + #[test] + fn test_internal_panic() { + setup_logging(); + assert_eq!( + secgate::SecGateReturn::Success(1), + montest_lib::test_internal_panic(true) + ); + } + + #[test] + fn test_lib_ctors() { + setup_logging(); + assert_eq!( + secgate::SecGateReturn::Success(true), + montest_lib::test_was_ctor_run() + ); + } + + #[test] + fn test_bin_ctors() { + setup_logging(); + assert_eq!(true, WAS_CTOR_RUN.load(Ordering::SeqCst)) + } +} + +static WAS_CTOR_RUN: AtomicBool = AtomicBool::new(false); + +#[used] +#[doc(hidden)] +#[allow(non_upper_case_globals)] +#[link_section = ".init_array"] +static ___cons_test___ctor: unsafe extern "C" fn() = { + #[allow(non_snake_case)] + #[link_section = ".text.startup"] + unsafe extern "C" fn ___cons_test___ctor() { + cons_test() + } + ___cons_test___ctor +}; +unsafe fn cons_test() { + WAS_CTOR_RUN.store(true, Ordering::SeqCst); +} diff --git a/src/runtime/rt-impl/Cargo.toml b/src/runtime/rt-impl/Cargo.toml new file mode 100644 index 00000000..328de74b --- /dev/null +++ b/src/runtime/rt-impl/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "twz-rt" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +twizzler-rt-abi = { path = "../../abi/rt-abi" } +thiserror = "1.0" +tracing = { version = "0.1", features = ["attributes"] } +tracing-subscriber = { version = "0.3" } +twizzler-abi = { path = "../../lib/twizzler-abi", default-features = false } +dynlink = { path = "../dynlink" } +bitflags = "2.4" +talc = { version = "3.1", default-features = false } +lazy_static = "1.4" +atomic = "0.6" +elf = "0.7" +static_assertions = "1.1" +monitor-api = { path = "../monitor-api" } +secgate = { path = "../secgate" } +stable-vec = "0.4.1" +lru = "0.12.4" +paste = "1" +printf-compat = { version = "0.1", default-features = false } diff --git a/src/runtime/rt/src/arch.rs b/src/runtime/rt-impl/src/arch.rs similarity index 100% rename from src/runtime/rt/src/arch.rs rename to src/runtime/rt-impl/src/arch.rs diff --git a/src/runtime/rt/src/arch/aarch64.rs b/src/runtime/rt-impl/src/arch/aarch64.rs similarity index 100% rename from src/runtime/rt/src/arch/aarch64.rs rename to src/runtime/rt-impl/src/arch/aarch64.rs diff --git a/src/runtime/rt-impl/src/arch/x86_64.rs b/src/runtime/rt-impl/src/arch/x86_64.rs new file mode 100644 index 00000000..92623424 --- /dev/null +++ b/src/runtime/rt-impl/src/arch/x86_64.rs @@ -0,0 +1,17 @@ +use twizzler_abi::upcall::{UpcallData, UpcallFrame}; + +#[no_mangle] +pub(crate) unsafe extern "C-unwind" fn twz_rt_upcall_entry_c( + rdi: *mut UpcallFrame, + rsi: *const UpcallData, +) -> ! { + use twizzler_abi::{syscall::sys_thread_exit, upcall::UPCALL_EXIT_CODE}; + + let handler = || crate::runtime::upcall::upcall_rust_entry(&mut *rdi, &*rsi); + + if std::panic::catch_unwind(handler).is_err() { + sys_thread_exit(UPCALL_EXIT_CODE); + } + // TODO: with uiret instruction, we may be able to avoid the kernel, here. + twizzler_abi::syscall::sys_thread_resume_from_upcall(&*rdi); +} diff --git a/src/runtime/rt/src/error.rs b/src/runtime/rt-impl/src/error.rs similarity index 100% rename from src/runtime/rt/src/error.rs rename to src/runtime/rt-impl/src/error.rs diff --git a/src/runtime/rt-impl/src/lib.rs b/src/runtime/rt-impl/src/lib.rs new file mode 100644 index 00000000..7ffcc1f0 --- /dev/null +++ b/src/runtime/rt-impl/src/lib.rs @@ -0,0 +1,29 @@ +//! # The Twizzler Reference Runtime +//! +//! This is a work in progress. + +#![allow(internal_features)] +#![feature(core_intrinsics)] +#![feature(thread_local)] +#![feature(fmt_internals)] +#![feature(array_windows)] +#![feature(unboxed_closures)] +#![feature(allocator_api)] +#![feature(hash_extract_if)] +#![feature(btree_extract_if)] +#![feature(naked_functions)] +#![feature(c_variadic)] +#![feature(linkage)] + +pub(crate) mod arch; + +mod runtime; +pub use runtime::{set_upcall_handler, RuntimeState, OUR_RUNTIME}; + +mod error; +pub use error::*; + +pub mod preinit; + +#[allow(non_snake_case)] +pub mod syms; diff --git a/src/runtime/rt/src/preinit.rs b/src/runtime/rt-impl/src/preinit.rs similarity index 91% rename from src/runtime/rt/src/preinit.rs rename to src/runtime/rt-impl/src/preinit.rs index 20c30609..f464fdc7 100644 --- a/src/runtime/rt/src/preinit.rs +++ b/src/runtime/rt-impl/src/preinit.rs @@ -14,12 +14,10 @@ impl fmt::Write for PreinitLogger { } } -static mut PREINIT_OUTPUT: PreinitLogger = PreinitLogger; - #[doc(hidden)] pub fn _print_normal(args: core::fmt::Arguments) { use fmt::Write; - let _ = unsafe { &mut PREINIT_OUTPUT }.write_fmt(args); + let _ = PreinitLogger.write_fmt(args); } #[macro_export] @@ -44,7 +42,10 @@ macro_rules! preinit_println { #[track_caller] pub fn preinit_abort() -> ! { - core::intrinsics::abort() + #[allow(unused_unsafe)] + unsafe { + core::intrinsics::abort() + } } #[track_caller] diff --git a/src/runtime/rt/src/runtime.rs b/src/runtime/rt-impl/src/runtime.rs similarity index 98% rename from src/runtime/rt/src/runtime.rs rename to src/runtime/rt-impl/src/runtime.rs index f5e35e72..c2ec5354 100644 --- a/src/runtime/rt/src/runtime.rs +++ b/src/runtime/rt-impl/src/runtime.rs @@ -16,7 +16,6 @@ mod thread; mod time; pub(crate) mod upcall; -pub use thread::RuntimeThreadControl; pub use upcall::set_upcall_handler; use self::object::ObjectHandleManager; diff --git a/src/runtime/rt/src/runtime/alloc.rs b/src/runtime/rt-impl/src/runtime/alloc.rs similarity index 90% rename from src/runtime/rt/src/runtime/alloc.rs rename to src/runtime/rt-impl/src/runtime/alloc.rs index 7d14e518..c40c470a 100644 --- a/src/runtime/rt/src/runtime/alloc.rs +++ b/src/runtime/rt-impl/src/runtime/alloc.rs @@ -19,15 +19,17 @@ use std::{ const MIN_ALIGN: usize = 16; use talc::{OomHandler, Span, Talc}; -use tracing::warn; use twizzler_abi::{ - object::{ObjID, MAX_SIZE, NULLPAGE_SIZE}, - syscall::{sys_object_create, BackingType, LifetimeType, ObjectCreate, ObjectCreateFlags}, + object::{ObjID, Protections, MAX_SIZE, NULLPAGE_SIZE}, + syscall::{ + sys_object_create, sys_object_map, BackingType, LifetimeType, ObjectCreate, + ObjectCreateFlags, + }, }; use twizzler_rt_abi::object::MapFlags; use super::{ReferenceRuntime, OUR_RUNTIME}; -use crate::{preinit_println, runtime::RuntimeState}; +use crate::runtime::RuntimeState; static LOCAL_ALLOCATOR: LocalAllocator = LocalAllocator { runtime: &OUR_RUNTIME, @@ -69,9 +71,8 @@ struct RuntimeOom { objects: Vec<(usize, ObjID), FailAlloc>, } -fn delete_obj(_id: ObjID) { - // TODO - warn!("unimplemented: delete object due to failure in allocator"); +fn release_object(id: ObjID) { + monitor_api::monitor_rt_object_unmap(id, MapFlags::READ | MapFlags::WRITE).unwrap(); } fn create_and_map() -> Option<(usize, ObjID)> { @@ -87,6 +88,20 @@ fn create_and_map() -> Option<(usize, ObjID)> { ) .ok()?; + if OUR_RUNTIME.state().contains(RuntimeState::IS_MONITOR) { + // Map directly, avoiding complex machinery in the monitor that depends on an allocator. + let slot = OUR_RUNTIME.allocate_slot().unwrap(); + sys_object_map( + None, + id, + slot, + Protections::READ | Protections::WRITE, + twizzler_abi::syscall::MapFlags::empty(), + ) + .unwrap(); + return Some((slot, id)); + } + let slot = monitor_api::monitor_rt_object_map(id, MapFlags::READ | MapFlags::WRITE) .unwrap() .ok(); @@ -94,7 +109,7 @@ fn create_and_map() -> Option<(usize, ObjID)> { if let Some(slot) = slot { Some((slot.slot, id)) } else { - delete_obj(id); + release_object(id); None } } @@ -116,8 +131,7 @@ impl OomHandler for RuntimeOom { .claim(Span::new(base as *mut _, top as *mut _)) .is_err() { - delete_obj(id); - monitor_api::monitor_rt_object_unmap(id, MapFlags::READ | MapFlags::WRITE).unwrap(); + release_object(id); return Err(()); } } diff --git a/src/runtime/rt/src/runtime/core.rs b/src/runtime/rt-impl/src/runtime/core.rs similarity index 65% rename from src/runtime/rt/src/runtime/core.rs rename to src/runtime/rt-impl/src/runtime/core.rs index a4127155..e869f83a 100644 --- a/src/runtime/rt/src/runtime/core.rs +++ b/src/runtime/rt-impl/src/runtime/core.rs @@ -1,7 +1,15 @@ //! Implements the core runtime functions. +use std::{ + collections::BTreeMap, + ffi::{c_char, c_void, CStr, CString}, + sync::{Mutex, OnceLock}, +}; + use dynlink::context::runtime::RuntimeInitInfo; -use monitor_api::SharedCompConfig; +use monitor_api::{RuntimeThreadControl, SharedCompConfig}; +use secgate::SecGateReturn; +use tracing::Level; use twizzler_abi::upcall::{UpcallFlags, UpcallInfo, UpcallMode, UpcallOptions, UpcallTarget}; use twizzler_rt_abi::{ core::{ @@ -17,11 +25,13 @@ use crate::{ preinit::{preinit_abort, preinit_unwrap}, preinit_println, runtime::RuntimeState, - RuntimeThreadControl, }; -#[thread_local] -static TLS_TEST: usize = 3222; +#[derive(Copy, Clone)] +struct PtrToInfo(*mut c_void); +unsafe impl Send for PtrToInfo {} +unsafe impl Sync for PtrToInfo {} +static MON_RTINFO: OnceLock> = OnceLock::new(); impl ReferenceRuntime { pub fn default_allocator(&self) -> &'static dyn std::alloc::GlobalAlloc { @@ -46,6 +56,30 @@ impl ReferenceRuntime { } } + pub fn is_monitor(&self) -> Option<*mut c_void> { + MON_RTINFO + .get() + .as_ref() + .unwrap() + .map(|x| x.0 as *mut _ as *mut c_void) + } + + pub fn cgetenv(&self, name: &CStr) -> *const c_char { + // TODO: this approach is very simple, but it leaks if the environment changes a lot. + static ENVMAP: Mutex> = Mutex::new(BTreeMap::new()); + let Ok(name) = name.to_str() else { + return core::ptr::null(); + }; + let Ok(val) = std::env::var(name) else { + return core::ptr::null(); + }; + let mut envmap = ENVMAP.lock().unwrap(); + envmap + .entry(val.to_string()) + .or_insert_with(|| CString::new(val.to_string()).unwrap()) + .as_ptr() + } + pub fn runtime_entry( &self, rtinfo: *const RuntimeInfo, @@ -62,6 +96,7 @@ impl ReferenceRuntime { .as_ref() .unwrap() }; + let _ = MON_RTINFO.set(Some(PtrToInfo(init_info as *const _ as *mut _))); self.init_for_monitor(init_info); } RUNTIME_INIT_COMP => { @@ -73,6 +108,7 @@ impl ReferenceRuntime { .as_ref() .unwrap() }; + let _ = MON_RTINFO.set(None); self.init_for_compartment(init_info); } x => { @@ -92,16 +128,31 @@ impl ReferenceRuntime { } pub fn pre_main_hook(&self) -> Option { - preinit_println!("====== {}", TLS_TEST); + // TODO: control this with env vars + tracing::subscriber::set_global_default( + tracing_subscriber::fmt() + .with_max_level(Level::INFO) + .finish(), + ) + .unwrap(); if self.state().contains(RuntimeState::IS_MONITOR) { self.init_slots(); + None } else { unsafe { self.set_runtime_ready() }; + let ret = match monitor_api::monitor_rt_comp_ctrl( + monitor_api::MonitorCompControlCmd::RuntimeReady, + ) { + SecGateReturn::Success(ret) => ret, + _ => self.abort(), + }; + ret } - None } - pub fn post_main_hook(&self) {} + pub fn post_main_hook(&self) { + monitor_api::monitor_rt_comp_ctrl(monitor_api::MonitorCompControlCmd::RuntimePostMain); + } pub fn sysinfo(&self) -> SystemInfo { let info = twizzler_abi::syscall::sys_info(); @@ -121,8 +172,11 @@ impl ReferenceRuntime { impl ReferenceRuntime { fn init_for_monitor(&self, init_info: &RuntimeInitInfo) { let upcall_target = UpcallTarget::new( - Some(crate::arch::rr_upcall_entry), - Some(crate::arch::rr_upcall_entry), + Some( + twizzler_rt_abi::arch::__twz_rt_upcall_entry + as unsafe extern "C-unwind" fn(_, _) -> !, + ), + Some(twizzler_rt_abi::arch::__twz_rt_upcall_entry), 0, 0, 0, @@ -150,11 +204,9 @@ impl ReferenceRuntime { .ok(), ); } - let tls = preinit_unwrap( - preinit_unwrap(TLS_GEN_MGR.lock().ok()) - .get_next_tls_info(None, || RuntimeThreadControl::new(0)), - ); - twizzler_abi::syscall::sys_thread_settls(tls as u64); + let mut tg = preinit_unwrap(TLS_GEN_MGR.write().ok()); + let tls = tg.get_next_tls_info(None, || RuntimeThreadControl::new(0)); + twizzler_abi::syscall::sys_thread_settls(preinit_unwrap(tls) as u64); if !init_info.ctor_set_array.is_null() && init_info.ctor_set_len != 0 { let ctor_slice = unsafe { @@ -195,3 +247,30 @@ impl ReferenceRuntime { twizzler_abi::syscall::sys_thread_settls(tls as u64); } } + +#[allow(improper_ctypes)] +extern "C" { + fn twizzler_call_lang_start( + main: fn(), + argc: isize, + argv: *const *const u8, + sigpipe: u8, + ) -> isize; +} + +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn main(argc: i32, argv: *const *const u8) -> i32 { + //TODO: sigpipe? + unsafe { twizzler_call_lang_start(dead_end, argc as isize, argv, 0) as i32 } +} + +fn dead_end() { + twizzler_abi::syscall::sys_thread_exit(0); +} + +// TODO: we should probably get this for real. +#[cfg(not(test))] +#[no_mangle] +#[linkage = "weak"] +pub extern "C" fn _init() {} diff --git a/src/runtime/rt-impl/src/runtime/debug.rs b/src/runtime/rt-impl/src/runtime/debug.rs new file mode 100644 index 00000000..b961699e --- /dev/null +++ b/src/runtime/rt-impl/src/runtime/debug.rs @@ -0,0 +1,79 @@ +use std::{collections::BTreeMap, sync::Mutex}; + +use monitor_api::{CompartmentHandle, LibraryHandle}; +use twizzler_abi::object::NULLPAGE_SIZE; +use twizzler_rt_abi::{ + bindings::{dl_phdr_info, loaded_image, loaded_image_id}, + object::MapFlags, +}; + +use super::ReferenceRuntime; + +static LIBNAMES: Mutex> = Mutex::new(BTreeMap::new()); + +impl ReferenceRuntime { + fn find_comp_dep_lib(&self, id: loaded_image_id) -> Option<(Option, LibraryHandle)> { + let n = id as usize; + let current = CompartmentHandle::current(); + if let Some(image) = current.libs().nth(n) { + return Some((None, image)); + } + let Some(mut n) = n.checked_sub(current.info().nr_libs) else { + return None; + }; + for dep in current.deps() { + if let Some(image) = dep.libs().nth(n) { + let name = dep.info().name.clone(); + return Some((Some(name), image)); + } + n = match n.checked_sub(dep.info().nr_libs) { + Some(rem) => rem, + None => return None, + }; + } + None + } + + pub fn get_image_info(&self, id: loaded_image_id) -> Option { + let (cn, lib) = self.find_comp_dep_lib(id)?; + let mut info = lib.info(); + tracing::trace!("get_image_info: {:?}", info); + let mut lib_names = LIBNAMES.lock().ok()?; + let fullname = if let Some(cn) = cn { + format!("{}::{}", cn, info.name) + } else { + info.name.clone() + }; + if !lib_names.contains_key(&fullname) { + let mut name_bytes = fullname.clone().into_bytes(); + name_bytes.push(0); + lib_names.insert(fullname.clone(), name_bytes.leak()); + } + let name_ptr = lib_names.get(&fullname)?.as_ptr(); + info.dl_info.name = name_ptr.cast(); + let handle = self.map_object(info.objid, MapFlags::READ).ok()?; + Some(loaded_image { + image_start: unsafe { handle.start().add(NULLPAGE_SIZE).cast() }, + image_len: handle.valid_len(), + image_handle: handle.into_raw(), + dl_info: info.dl_info, + id, + }) + } + + pub fn iterate_phdr( + &self, + f: &mut dyn FnMut(dl_phdr_info) -> core::ffi::c_int, + ) -> core::ffi::c_int { + let mut n = 0; + let mut ret = 0; + while let Some(image) = self.get_image_info(n) { + ret = f(image.dl_info); + if ret != 0 { + return ret; + } + n += 1; + } + ret + } +} diff --git a/src/runtime/rt/src/runtime/file.rs b/src/runtime/rt-impl/src/runtime/file.rs similarity index 97% rename from src/runtime/rt/src/runtime/file.rs rename to src/runtime/rt-impl/src/runtime/file.rs index 241df51f..93f8f1aa 100644 --- a/src/runtime/rt/src/runtime/file.rs +++ b/src/runtime/rt-impl/src/runtime/file.rs @@ -48,7 +48,7 @@ const WRITABLE_BYTES: u64 = (1 << 26) - size_of::() as u64 - NULLP const OBJECT_COUNT: usize = 256; const DIRECT_OBJECT_COUNT: usize = 255; // The number of objects reachable from the direct pointer list const MAX_FILE_SIZE: u64 = WRITABLE_BYTES * 256; -const MAX_LOADABLE_OBJECTS: usize = 16; +const _MAX_LOADABLE_OBJECTS: usize = 16; lazy_static! { static ref FD_SLOTS: Mutex> = Mutex::new(StableVec::from([ FdKind::Stdio, @@ -197,7 +197,7 @@ impl ReferenceRuntime { fd: RawFd, off: Option, buf: &mut [u8], - flags: IoFlags, + _flags: IoFlags, ) -> Result { if off.is_some() { return Err(IoError::SeekError); @@ -210,7 +210,7 @@ impl ReferenceRuntime { fd: RawFd, off: Option, buf: &[u8], - flags: IoFlags, + _flags: IoFlags, ) -> Result { if off.is_some() { return Err(IoError::SeekError); @@ -220,20 +220,20 @@ impl ReferenceRuntime { pub fn fd_pwritev( &self, - fd: RawFd, - off: Option, - buf: &[io_vec], - flags: IoFlags, + _fd: RawFd, + _off: Option, + _buf: &[io_vec], + _flags: IoFlags, ) -> Result { return Err(IoError::Other); } pub fn fd_preadv( &self, - fd: RawFd, - off: Option, - buf: &[io_vec], - flags: IoFlags, + _fd: RawFd, + _off: Option, + _buf: &[io_vec], + _flags: IoFlags, ) -> Result { return Err(IoError::Other); } @@ -353,7 +353,7 @@ impl ReferenceRuntime { } pub fn close(&self, fd: RawFd) -> Option<()> { - let file_desc = get_fd_slots() + let _file_desc = get_fd_slots() .lock() .unwrap() .remove(fd.try_into().unwrap())?; diff --git a/src/runtime/rt/src/runtime/object.rs b/src/runtime/rt-impl/src/runtime/object.rs similarity index 99% rename from src/runtime/rt/src/runtime/object.rs rename to src/runtime/rt-impl/src/runtime/object.rs index 9ddc86fa..346f1b22 100644 --- a/src/runtime/rt/src/runtime/object.rs +++ b/src/runtime/rt-impl/src/runtime/object.rs @@ -1,6 +1,5 @@ use std::{ collections::HashMap, - ptr::NonNull, sync::atomic::{AtomicU64, AtomicUsize}, }; @@ -31,7 +30,7 @@ fn mapflags_into_prot(flags: MapFlags) -> Protections { } #[repr(C)] -struct RuntimeHandleInfo { +pub(crate) struct RuntimeHandleInfo { refs: AtomicU64, } diff --git a/src/runtime/rt/src/runtime/process.rs b/src/runtime/rt-impl/src/runtime/process.rs similarity index 100% rename from src/runtime/rt/src/runtime/process.rs rename to src/runtime/rt-impl/src/runtime/process.rs diff --git a/src/runtime/rt/src/runtime/slot.rs b/src/runtime/rt-impl/src/runtime/slot.rs similarity index 100% rename from src/runtime/rt/src/runtime/slot.rs rename to src/runtime/rt-impl/src/runtime/slot.rs diff --git a/src/runtime/rt/src/runtime/thread.rs b/src/runtime/rt-impl/src/runtime/thread.rs similarity index 94% rename from src/runtime/rt/src/runtime/thread.rs rename to src/runtime/rt-impl/src/runtime/thread.rs index 4c2577ba..6199fd2a 100644 --- a/src/runtime/rt/src/runtime/thread.rs +++ b/src/runtime/rt-impl/src/runtime/thread.rs @@ -1,7 +1,6 @@ //! Implements thread management routines. use dynlink::tls::Tcb; -use lazy_static::lazy_static; use twizzler_abi::syscall::{ sys_thread_sync, sys_thread_yield, ThreadSync, ThreadSyncError, ThreadSyncFlags, ThreadSyncOp, ThreadSyncReference, ThreadSyncSleep, ThreadSyncWake, @@ -16,14 +15,11 @@ mod internal; mod mgr; mod tcb; -pub use tcb::RuntimeThreadControl; pub(crate) use tcb::TLS_GEN_MGR; const MIN_STACK_ALIGN: usize = 128; -lazy_static! { - static ref THREAD_MGR: ThreadManager = ThreadManager::new(); -} +static THREAD_MGR: ThreadManager = ThreadManager::new(); impl ReferenceRuntime { pub fn available_parallelism(&self) -> core::num::NonZeroUsize { diff --git a/src/runtime/rt/src/runtime/thread/internal.rs b/src/runtime/rt-impl/src/runtime/thread/internal.rs similarity index 96% rename from src/runtime/rt/src/runtime/thread/internal.rs rename to src/runtime/rt-impl/src/runtime/thread/internal.rs index e9fc2520..2f92dfaa 100644 --- a/src/runtime/rt/src/runtime/thread/internal.rs +++ b/src/runtime/rt-impl/src/runtime/thread/internal.rs @@ -7,11 +7,11 @@ use std::{ }; use dynlink::tls::Tcb; +use monitor_api::RuntimeThreadControl; use tracing::trace; use twizzler_abi::{object::NULLPAGE_SIZE, thread::ThreadRepr}; use twizzler_rt_abi::{object::ObjectHandle, thread::ThreadSpawnArgs}; -use super::RuntimeThreadControl; use crate::runtime::{thread::MIN_STACK_ALIGN, OUR_RUNTIME}; /// Internal representation of a thread, tracking the resources @@ -79,7 +79,7 @@ impl Drop for InternalThread { // Args is allocated by a box. let _args = Box::from_raw(self.args_box as *mut ThreadSpawnArgs); drop(_args); - tracing::warn!("TODO: drop TLS"); + tracing::debug!("TODO: drop TLS"); } } } diff --git a/src/runtime/rt/src/runtime/thread/mgr.rs b/src/runtime/rt-impl/src/runtime/thread/mgr.rs similarity index 84% rename from src/runtime/rt/src/runtime/thread/mgr.rs rename to src/runtime/rt-impl/src/runtime/thread/mgr.rs index 02268f83..a9f89b2e 100644 --- a/src/runtime/rt/src/runtime/thread/mgr.rs +++ b/src/runtime/rt-impl/src/runtime/thread/mgr.rs @@ -1,10 +1,12 @@ //! Thread management routines, including spawn and join. -use std::{alloc::Layout, collections::HashMap, sync::Mutex}; +use std::{alloc::Layout, collections::BTreeMap}; +use monitor_api::RuntimeThreadControl; use tracing::trace; use twizzler_abi::{ object::{ObjID, NULLPAGE_SIZE}, + simple_mutex::Mutex, thread::{ExecutionState, ThreadRepr}, }; use twizzler_rt_abi::{ @@ -15,32 +17,32 @@ use twizzler_rt_abi::{ use super::internal::InternalThread; use crate::runtime::{ thread::{ - tcb::{trampoline, RuntimeThreadControl, TLS_GEN_MGR}, + tcb::{trampoline, TLS_GEN_MGR}, MIN_STACK_ALIGN, THREAD_MGR, }, ReferenceRuntime, OUR_RUNTIME, }; -pub(super) struct ThreadManager { +pub(crate) struct ThreadManager { inner: Mutex, } impl ThreadManager { - pub(super) fn new() -> Self { + pub(super) const fn new() -> Self { Self { inner: Mutex::new(ThreadManagerInner::new()), } } pub fn with_internal R>(&self, id: u32, f: F) -> Option { - let inner = self.inner.lock().unwrap(); + let inner = self.inner.lock(); Some(f(inner.all_threads.get(&id)?)) } } #[derive(Default)] struct ThreadManagerInner { - all_threads: HashMap, + all_threads: BTreeMap, // Threads that have exited, but we haven't cleaned up yet. to_cleanup: Vec, // Basic unique-ID system. @@ -52,10 +54,12 @@ unsafe impl Send for ThreadManager {} unsafe impl Sync for ThreadManager {} impl ThreadManagerInner { - fn new() -> Self { + const fn new() -> Self { Self { next_id: 1, - ..Default::default() + all_threads: BTreeMap::new(), + to_cleanup: vec![], + id_stack: vec![], } } @@ -122,6 +126,19 @@ impl<'a> Drop for IdDropper<'a> { } impl ReferenceRuntime { + pub fn cross_compartment_entry(&self) { + twizzler_abi::syscall::sys_thread_settls(0); + let mut inner = THREAD_MGR.inner.lock(); + let id = inner.next_id().freeze(); + drop(inner); + let tls = TLS_GEN_MGR + .write() + .unwrap() + .get_next_tls_info(None, || RuntimeThreadControl::new(id)) + .unwrap(); + twizzler_abi::syscall::sys_thread_settls(tls as u64); + } + pub(super) fn impl_spawn( &self, args: twizzler_rt_abi::thread::ThreadSpawnArgs, @@ -129,7 +146,7 @@ impl ReferenceRuntime { // Box this up so we can pass it to the new thread. let args = Box::new(args); let tls = TLS_GEN_MGR - .lock() + .write() .unwrap() .get_next_tls_info(None, || RuntimeThreadControl::new(0)) .unwrap(); @@ -141,7 +158,7 @@ impl ReferenceRuntime { // Take the thread management lock, so that when the new thread starts we cannot observe // that thread running without the management data being recorded. - let mut inner = THREAD_MGR.inner.lock().unwrap(); + let mut inner = THREAD_MGR.inner.lock(); let id = inner.next_id(); // Set the thread's ID. After this the TCB is ready. @@ -201,7 +218,7 @@ impl ReferenceRuntime { ) -> Result<(), JoinError> { trace!("joining on thread {} with timeout {:?}", id, timeout); let repr = { - let mut inner = THREAD_MGR.inner.lock().unwrap(); + let mut inner = THREAD_MGR.inner.lock(); inner.scan_for_exited_except(id); inner .all_threads @@ -215,7 +232,7 @@ impl ReferenceRuntime { loop { let (state, _code) = base.wait(timeout).ok_or(JoinError::Timeout)?; if state == ExecutionState::Exited { - let mut inner = THREAD_MGR.inner.lock().unwrap(); + let mut inner = THREAD_MGR.inner.lock(); inner.prep_cleanup(id); inner.do_thread_gc(); trace!("join {} completed", id); diff --git a/src/runtime/rt/src/runtime/thread/tcb.rs b/src/runtime/rt-impl/src/runtime/thread/tcb.rs similarity index 59% rename from src/runtime/rt/src/runtime/thread/tcb.rs rename to src/runtime/rt-impl/src/runtime/thread/tcb.rs index a298d35d..e24571de 100644 --- a/src/runtime/rt/src/runtime/thread/tcb.rs +++ b/src/runtime/rt-impl/src/runtime/thread/tcb.rs @@ -7,89 +7,16 @@ use std::{ alloc::GlobalAlloc, - cell::UnsafeCell, - collections::HashMap, + collections::BTreeMap, panic::catch_unwind, - sync::{ - atomic::{AtomicU32, Ordering}, - Mutex, - }, + sync::{atomic::Ordering, RwLock}, }; use dynlink::tls::Tcb; -use monitor_api::TlsTemplateInfo; +use monitor_api::{RuntimeThreadControl, TlsTemplateInfo, THREAD_STARTED}; use tracing::trace; -use crate::{preinit_println, runtime::OUR_RUNTIME}; - -const THREAD_STARTED: u32 = 1; -pub struct RuntimeThreadControl { - // Need to keep a lock for the ID, though we don't expect to use it much. - internal_lock: AtomicU32, - flags: AtomicU32, - id: UnsafeCell, -} - -impl Default for RuntimeThreadControl { - fn default() -> Self { - Self::new(0) - } -} - -impl RuntimeThreadControl { - pub const fn new(id: u32) -> Self { - Self { - internal_lock: AtomicU32::new(0), - flags: AtomicU32::new(0), - id: UnsafeCell::new(id), - } - } - - fn write_lock(&self) { - loop { - let old = self.internal_lock.fetch_or(1, Ordering::Acquire); - if old == 0 { - break; - } - } - } - - fn write_unlock(&self) { - self.internal_lock.fetch_and(!1, Ordering::Release); - } - - fn read_lock(&self) { - loop { - let old = self.internal_lock.fetch_add(2, Ordering::Acquire); - // If this happens, something has gone very wrong. - if old > i32::MAX as u32 { - OUR_RUNTIME.abort(); - } - if old & 1 == 0 { - break; - } - } - } - - fn read_unlock(&self) { - self.internal_lock.fetch_sub(2, Ordering::Release); - } - - pub fn set_id(&self, id: u32) { - self.write_lock(); - unsafe { - *self.id.get().as_mut().unwrap() = id; - } - self.write_unlock(); - } - - pub fn id(&self) -> u32 { - self.read_lock(); - let id = unsafe { *self.id.get().as_ref().unwrap() }; - self.read_unlock(); - id - } -} +use crate::runtime::OUR_RUNTIME; /// Run a closure using the current thread's control struct as the argument. pub(super) fn with_current_thread R>(f: F) -> R { @@ -112,10 +39,6 @@ pub(super) extern "C" fn trampoline(arg: usize) -> ! { cur.flags.fetch_or(THREAD_STARTED, Ordering::SeqCst); trace!("thread {} started", cur.id()); }); - twizzler_abi::syscall::sys_kernel_console_write( - b"alive\n", - twizzler_abi::syscall::KernelConsoleWriteFlags::empty(), - ); // Find the arguments. arg is a pointer to a Box::into_raw of a Box of ThreadSpawnArgs. let arg = unsafe { (arg as *const twizzler_rt_abi::thread::ThreadSpawnArgs) @@ -134,7 +57,7 @@ pub(super) extern "C" fn trampoline(arg: usize) -> ! { #[derive(Default)] pub(crate) struct TlsGenMgr { - map: HashMap, + map: BTreeMap, } pub(crate) struct TlsGen { @@ -144,11 +67,17 @@ pub(crate) struct TlsGen { unsafe impl Send for TlsGen {} -lazy_static::lazy_static! { -pub(crate) static ref TLS_GEN_MGR: Mutex = Mutex::new(TlsGenMgr::default()); -} +pub(crate) static TLS_GEN_MGR: RwLock = RwLock::new(TlsGenMgr { + map: BTreeMap::new(), +}); impl TlsGenMgr { + pub fn _need_new_gen(&self, mygen: Option) -> bool { + let cc = monitor_api::get_comp_config(); + let template = unsafe { cc.get_tls_template().as_ref().unwrap() }; + mygen.is_some_and(|mygen| mygen == template.gen) + } + pub fn get_next_tls_info( &mut self, mygen: Option, diff --git a/src/runtime/rt/src/runtime/time.rs b/src/runtime/rt-impl/src/runtime/time.rs similarity index 100% rename from src/runtime/rt/src/runtime/time.rs rename to src/runtime/rt-impl/src/runtime/time.rs diff --git a/src/runtime/rt/src/runtime/upcall.rs b/src/runtime/rt-impl/src/runtime/upcall.rs similarity index 56% rename from src/runtime/rt/src/runtime/upcall.rs rename to src/runtime/rt-impl/src/runtime/upcall.rs index f0d19f7d..ed550779 100644 --- a/src/runtime/rt/src/runtime/upcall.rs +++ b/src/runtime/rt-impl/src/runtime/upcall.rs @@ -1,20 +1,25 @@ -use std::sync::OnceLock; +use std::{ffi::c_void, sync::OnceLock}; use twizzler_abi::upcall::{UpcallData, UpcallFrame, UpcallHandlerFlags}; pub(crate) fn upcall_rust_entry(frame: &mut UpcallFrame, info: &UpcallData) { let imp = UPCALL_IMPL.get(); - if let Some(imp) = imp { - imp(frame, info) + if let Some(Some(imp)) = imp { + unsafe { + imp( + frame as *mut _ as *mut c_void, + info as *const _ as *const c_void, + ) + } } else { upcall_def_handler(frame, info) } } -pub type HandlerType = &'static (dyn Fn(&mut UpcallFrame, &UpcallData) + Send + Sync + 'static); -static UPCALL_IMPL: OnceLock = OnceLock::new(); +pub type HandlerType = unsafe extern "C-unwind" fn(frame: *mut c_void, info: *const c_void); +static UPCALL_IMPL: OnceLock> = OnceLock::new(); -pub fn set_upcall_handler(handler: HandlerType) -> Result<(), HandlerSetError> { +pub fn set_upcall_handler(handler: Option) -> Result<(), HandlerSetError> { UPCALL_IMPL.set(handler).map_err(|_| HandlerSetError) } diff --git a/src/runtime/rt/src/syms.rs b/src/runtime/rt-impl/src/syms.rs similarity index 92% rename from src/runtime/rt/src/syms.rs rename to src/runtime/rt-impl/src/syms.rs index 5d0f96e0..f3796e45 100644 --- a/src/runtime/rt/src/syms.rs +++ b/src/runtime/rt-impl/src/syms.rs @@ -69,13 +69,13 @@ macro_rules! check_ffi_type { }; } -use std::{ffi::CStr, os::fd::RawFd}; +use std::ffi::{c_void, CStr}; use tracing::warn; // core.h use twizzler_rt_abi::bindings::option_exit_code; -use crate::runtime::OUR_RUNTIME; +use crate::{runtime::OUR_RUNTIME, set_upcall_handler}; #[no_mangle] pub unsafe extern "C-unwind" fn twz_rt_abort() { @@ -125,10 +125,18 @@ check_ffi_type!(twz_rt_runtime_entry, _, _); #[no_mangle] pub unsafe extern "C-unwind" fn twz_rt_cross_compartment_entry() { - warn!("TODO: cross-compartment-entry"); + OUR_RUNTIME.cross_compartment_entry(); } check_ffi_type!(twz_rt_cross_compartment_entry); +#[no_mangle] +pub unsafe extern "C-unwind" fn twz_rt_set_upcall_handler( + handler: Option, +) { + let _ = set_upcall_handler(handler); +} +check_ffi_type!(twz_rt_set_upcall_handler, _); + // alloc.h use twizzler_rt_abi::bindings::{alloc_flags, ZERO_MEMORY}; @@ -584,11 +592,7 @@ pub unsafe extern "C-unwind" fn free(ptr: *mut core::ffi::c_void) { #[no_mangle] pub unsafe extern "C-unwind" fn getenv(name: *const core::ffi::c_char) -> *const core::ffi::c_char { let n = unsafe { CStr::from_ptr(name.cast()) }; - warn!( - "called c:getenv with name = {:p}: `{:?}`: not yet implemented", - name, n - ); - core::ptr::null() + OUR_RUNTIME.cgetenv(n) } #[no_mangle] @@ -634,3 +638,46 @@ pub unsafe extern "C-unwind" fn fprintf( ); bytes_written } + +#[no_mangle] +pub unsafe extern "C-unwind" fn __monitor_get_slot() -> isize { + match OUR_RUNTIME.allocate_slot() { + Some(s) => s.try_into().unwrap_or(-1), + None => -1, + } +} + +#[no_mangle] +pub unsafe extern "C-unwind" fn __monitor_release_slot(slot: usize) { + OUR_RUNTIME.release_slot(slot); +} + +#[no_mangle] +pub unsafe extern "C-unwind" fn __monitor_release_pair(one: usize, two: usize) { + OUR_RUNTIME.release_pair((one, two)); +} + +#[no_mangle] +pub unsafe extern "C-unwind" fn __monitor_get_slot_pair(one: *mut usize, two: *mut usize) -> bool { + let Some((a, b)) = OUR_RUNTIME.allocate_pair() else { + return false; + }; + one.write(a); + two.write(b); + true +} + +#[no_mangle] +pub unsafe extern "C-unwind" fn __monitor_ready() { + OUR_RUNTIME.set_runtime_ready(); +} + +#[no_mangle] +pub unsafe extern "C-unwind" fn __is_monitor_ready() -> bool { + OUR_RUNTIME.state().contains(crate::RuntimeState::READY) +} + +#[no_mangle] +pub unsafe extern "C-unwind" fn __is_monitor() -> *mut c_void { + OUR_RUNTIME.is_monitor().unwrap_or(core::ptr::null_mut()) +} diff --git a/src/runtime/rt/Cargo.toml b/src/runtime/rt/Cargo.toml index 8a867d91..56d2e51e 100644 --- a/src/runtime/rt/Cargo.toml +++ b/src/runtime/rt/Cargo.toml @@ -1,30 +1,8 @@ [package] -name = "twz-rt" +name = "twizzler-runtime" version = "0.1.0" edition = "2021" -[lib] -crate-type = ["dylib"] - [dependencies] twizzler-rt-abi = { path = "../../abi/rt-abi" } -thiserror = "1.0" -tracing = { version = "0.1", features = ["attributes"] } -twizzler-abi = { path = "../../lib/twizzler-abi", default-features = false } -dynlink = { path = "../dynlink" } -bitflags = "2.4" -talc = { version = "3.1", default-features = false } -lazy_static = "1.4" -atomic = "0.6" -elf = "0.7" -static_assertions = "1.1" -monitor-api = { path = "../monitor-api" } -secgate = { path = "../secgate" } -stable-vec = "0.4.1" -lru = "0.12.4" -paste = "1" -printf-compat = { version = "0.1", default-features = false } - -[features] -runtime = [] -default = ["runtime"] +twz-rt = { path = "../rt-impl" } diff --git a/src/runtime/rt/build.rs b/src/runtime/rt/build.rs new file mode 100644 index 00000000..32709e4f --- /dev/null +++ b/src/runtime/rt/build.rs @@ -0,0 +1,10 @@ +fn main() { + if let Ok(target) = std::env::var("TARGET") { + if let Ok(profile) = std::env::var("PROFILE") { + println!( + "cargo::rustc-link-search=target/dynamic/{}/{}", + target, profile + ); + } + } +} diff --git a/src/runtime/rt/src/arch/x86_64.rs b/src/runtime/rt/src/arch/x86_64.rs deleted file mode 100644 index 55e6a834..00000000 --- a/src/runtime/rt/src/arch/x86_64.rs +++ /dev/null @@ -1,41 +0,0 @@ -use twizzler_abi::upcall::{UpcallData, UpcallFrame}; - -#[cfg(feature = "runtime")] -#[no_mangle] -/// Entry for upcalls. -/// -/// # Safety -/// This function may not be called except as an upcall from the kernel. -pub unsafe extern "C-unwind" fn rr_upcall_entry( - rdi: *mut UpcallFrame, - rsi: *const UpcallData, -) -> ! { - core::arch::asm!( - "and rsp, 0xfffffffffffffff0", - "mov rbp, rdx", - "push rax", - "jmp rr_upcall_entry2", - in("rax") (*rdi).rip, - in("rdx") (*rdi).rbp, - in("rdi") rdi, - in("rsi") rsi, - options(noreturn) - ); -} - -#[cfg(feature = "runtime")] -#[no_mangle] -pub(crate) unsafe extern "C-unwind" fn rr_upcall_entry2( - rdi: *mut UpcallFrame, - rsi: *const UpcallData, -) -> ! { - use twizzler_abi::{syscall::sys_thread_exit, upcall::UPCALL_EXIT_CODE}; - - let handler = || crate::runtime::upcall::upcall_rust_entry(&mut *rdi, &*rsi); - - if std::panic::catch_unwind(handler).is_err() { - sys_thread_exit(UPCALL_EXIT_CODE); - } - // TODO: with uiret instruction, we may be able to avoid the kernel, here. - twizzler_abi::syscall::sys_thread_resume_from_upcall(&*rdi); -} diff --git a/src/runtime/rt/src/lib.rs b/src/runtime/rt/src/lib.rs index a4912922..f2c2d482 100644 --- a/src/runtime/rt/src/lib.rs +++ b/src/runtime/rt/src/lib.rs @@ -1,29 +1,2 @@ -//! # The Twizzler Reference Runtime -//! The Reference Runtime implements the Runtime trait from twizzler-runtime-abi, and is designed to -//! be the primary, fully supported programming environment on Twizzler. -//! -//! This is a work in progress. - -#![feature(core_intrinsics)] -#![feature(thread_local)] -#![feature(fmt_internals)] -#![feature(array_windows)] -#![feature(unboxed_closures)] -#![feature(allocator_api)] -#![feature(hash_extract_if)] -#![feature(naked_functions)] -#![feature(c_variadic)] - -pub(crate) mod arch; - -pub use arch::rr_upcall_entry; - -mod runtime; -pub use runtime::{set_upcall_handler, RuntimeState, RuntimeThreadControl, OUR_RUNTIME}; - -mod error; -pub use error::*; - -pub mod preinit; - -pub mod syms; +#[link(name = "twz_rt")] +extern "C-unwind" {} diff --git a/src/runtime/rt/src/runtime/debug.rs b/src/runtime/rt/src/runtime/debug.rs deleted file mode 100644 index ccaafdd2..00000000 --- a/src/runtime/rt/src/runtime/debug.rs +++ /dev/null @@ -1,40 +0,0 @@ -use twizzler_abi::object::NULLPAGE_SIZE; -use twizzler_rt_abi::{ - bindings::{dl_phdr_info, loaded_image, loaded_image_id}, - object::MapFlags, -}; - -use super::ReferenceRuntime; - -impl ReferenceRuntime { - pub fn get_image_info(&self, id: loaded_image_id) -> Option { - let lib = monitor_api::CompartmentHandle::current() - .libs() - .nth(id as usize)?; - let info = lib.info(); - let handle = self.map_object(info.objid, MapFlags::READ).ok()?; - Some(loaded_image { - image_start: unsafe { handle.start().add(NULLPAGE_SIZE).cast() }, - image_len: handle.valid_len(), - image_handle: handle.into_raw(), - dl_info: info.dl_info, - id, - }) - } - - pub fn iterate_phdr( - &self, - f: &mut dyn FnMut(dl_phdr_info) -> core::ffi::c_int, - ) -> core::ffi::c_int { - let mut n = 0; - let mut ret = 0; - while let Some(image) = self.get_image_info(n) { - ret = f(image.dl_info); - if ret != 0 { - return ret; - } - n += 1; - } - ret - } -} diff --git a/src/runtime/secgate/secgate-macros/src/lib.rs b/src/runtime/secgate/secgate-macros/src/lib.rs index 413d3046..f1f59dba 100644 --- a/src/runtime/secgate/secgate-macros/src/lib.rs +++ b/src/runtime/secgate/secgate-macros/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(c_str_literals)] #![feature(iterator_try_collect)] #![feature(proc_macro_diagnostic)] // syn doesn't allow us to easily fix this. @@ -167,12 +166,10 @@ fn handle_secure_gate( let Info { mod_name, - fn_name, internal_fn_name, .. } = names; tree.sig.ident = parse_quote!(#internal_fn_name); - //tree.vis = parse_quote!(pub(crate)); if entry_only { Ok(quote::quote! { @@ -208,7 +205,7 @@ fn handle_secure_gate( fn get_entry_sig(tree: &ItemFn) -> Signature { let mut sig = tree.sig.clone(); - sig.abi = parse_quote!( extern "C-unwind" ); + sig.abi = parse_quote!( extern "C" ); sig.inputs = Punctuated::new(); sig.inputs .push_value(parse_quote!(info: *const secgate::GateCallInfo)); @@ -229,7 +226,7 @@ fn build_trampoline(tree: &ItemFn, names: &Info) -> Result Result Result secgate::SecGateReturn::<_>::Success(r), + Ok(r) => secgate::SecGateReturn::Success(r), Err(_) => secgate::SecGateReturn::<_>::CalleePanic, }; @@ -391,7 +384,7 @@ fn build_public_call(tree: &ItemFn, names: &Info) -> Result::NoReturnValue) + ret.into_inner() }) }) }); @@ -481,7 +474,7 @@ fn build_types(tree: &ItemFn, names: &Info) -> Result { #[allow(non_camel_case_types)] type #entry_type_name = #ty; pub type Args = #arg_types; - pub type Ret = secgate::Return>; + pub type Ret = secgate::Return<#ret_type>; pub const ARGS_SIZE: usize = core::mem::size_of::(); pub const RET_SIZE: usize = core::mem::size_of::(); }) diff --git a/src/runtime/secgate/src/lib.rs b/src/runtime/secgate/src/lib.rs index dba82fd5..06afdc8c 100644 --- a/src/runtime/secgate/src/lib.rs +++ b/src/runtime/secgate/src/lib.rs @@ -5,8 +5,7 @@ #![feature(auto_traits)] #![feature(negative_impls)] #![feature(linkage)] -#![feature(core_intrinsics)] -#![feature(const_option)] +#![feature(maybe_uninit_as_bytes)] use core::ffi::CStr; use std::{cell::UnsafeCell, marker::Tuple, mem::MaybeUninit}; @@ -126,7 +125,7 @@ impl Arguments { #[repr(C)] pub struct Return { isset: bool, - ret: MaybeUninit, + ret: MaybeUninit>, } impl Clone for Return { @@ -152,11 +151,11 @@ impl Return { /// If a previous call to set is made, or this was constructed by new(), then into_inner /// returns the inner value. Otherwise, returns None. - pub fn into_inner(self) -> Option { + pub fn into_inner(self) -> SecGateReturn { if self.isset { - Some(unsafe { self.ret.assume_init() }) + unsafe { self.ret.assume_init() } } else { - None + SecGateReturn::NoReturnValue } } @@ -169,7 +168,7 @@ impl Return { } /// Set the inner value. Future call to into_inner will return Some(val). - pub fn set(&mut self, val: T) { + pub fn set(&mut self, val: SecGateReturn) { self.ret.write(val); self.isset = true; } @@ -229,7 +228,7 @@ impl GateCallInfo { /// Get the ID of the source context, or None if the call was not cross-context. pub fn source_context(&self) -> Option { - if self.src_ctx.as_u128() == 0 { + if self.src_ctx.raw() == 0 { None } else { Some(self.src_ctx) @@ -238,7 +237,7 @@ impl GateCallInfo { /// Get the ID of the calling thread. pub fn thread_id(&self) -> ObjID { - if self.thread_id.as_u128() == 0 { + if self.thread_id.raw() == 0 { twizzler_abi::syscall::sys_thread_self_id() } else { self.thread_id diff --git a/toolchain/src/bootstrap-include/inttypes.h b/toolchain/src/bootstrap-include/inttypes.h index 09a05dd1..74799c51 100644 --- a/toolchain/src/bootstrap-include/inttypes.h +++ b/toolchain/src/bootstrap-include/inttypes.h @@ -1,8 +1,8 @@ #pragma once -#define PRIu64 "%llu" -#define PRIx64 "%llx" -#define PRId64 "%lld" -#define PRIu8 "%u" -#define PRIxPTR "%p" -#define PRIdPTR "%ld" +#define PRIu64 "llu" +#define PRIx64 "llx" +#define PRId64 "lld" +#define PRIu8 "u" +#define PRIxPTR "p" +#define PRIdPTR "ld" diff --git a/toolchain/src/bootstrap-include/stdlib.h b/toolchain/src/bootstrap-include/stdlib.h index e511800c..dfd75796 100644 --- a/toolchain/src/bootstrap-include/stdlib.h +++ b/toolchain/src/bootstrap-include/stdlib.h @@ -13,7 +13,7 @@ _Noreturn static inline void abort(void) { } extern void *malloc(size_t len); -char *getenv(const char *name); +extern char *getenv(const char *name); extern void free(void *ptr); #ifdef __cplusplus diff --git a/tools/initrd_gen/src/main.rs b/tools/initrd_gen/src/main.rs index 1c951475..c1e07ef5 100644 --- a/tools/initrd_gen/src/main.rs +++ b/tools/initrd_gen/src/main.rs @@ -67,7 +67,7 @@ fn main() { archive.mode(tar::HeaderMode::Deterministic); for file in files.unwrap_or_default().map(|s| s.as_str()) { - let mut f = File::open(file).unwrap(); + let mut f = File::open(file).expect(&format!("failed to open file: {}", file)); archive .append_file( Path::new(file) diff --git a/tools/xtask/src/build.rs b/tools/xtask/src/build.rs index 476aee91..e16e37e9 100644 --- a/tools/xtask/src/build.rs +++ b/tools/xtask/src/build.rs @@ -250,7 +250,7 @@ fn build_twizzler<'a>( Ok(Some(cargo::ops::compile(workspace, &options)?)) } -fn maybe_build_tests<'a>( +fn maybe_build_tests_static<'a>( workspace: &'a Workspace, build_config: &crate::BuildConfig, static_compilation: &Option>, @@ -262,7 +262,7 @@ fn maybe_build_tests<'a>( } crate::toolchain::set_static(); crate::toolchain::set_cc(); - crate::print_status_line("collection: userspace::tests", Some(build_config)); + crate::print_status_line("collection: userspace::tests-static", Some(build_config)); let triple = Triple::new( build_config.arch, build_config.machine, @@ -279,6 +279,70 @@ fn maybe_build_tests<'a>( options.build_config.requested_profile = InternedString::new("release"); } // TODO: once we have switched to the new runtime, all these should be removed. + options.spec = Packages::Packages( + packages + .iter() + .filter_map(|p| match p.name().as_str() { + "twizzler-kernel-macros" => None, + "nvme" => None, + "twz-rt" => None, + "monitor" => None, + "monitor-api" => None, + "bootstrap" => None, + "dynlink" => None, + "layout" => None, + "lethe-gadget-fat" => None, + "secgate-macros" => None, + "layout-derive" => None, + "twizzler-rt-abi" => None, + "twizzler-types" => None, + "twizzler-runtime" => None, + "twizzler-object" => None, + "twizzler-nando" => None, + "twizzler-net" => None, + "twizzler-futures" => None, + "twizzler-async" => None, + "twizzler-queue" => None, + "montest-lib" => None, + "montest" => None, + _ => Some(p.name().to_string()), + }) + .collect(), + ); + options.build_config.force_rebuild = other_options.needs_full_rebuild; + Ok(Some(cargo::ops::compile(workspace, &options)?)) +} + +// Once we merge the runtimes fully and switch to using the dynamic runtime as default, +// we can merge a lot of this test infrastructure. +fn maybe_build_tests_dynamic<'a>( + workspace: &'a Workspace, + build_config: &crate::BuildConfig, + static_compilation: &Option>, + other_options: &OtherOptions, +) -> anyhow::Result>> { + let mode = CompileMode::Test; + if !other_options.build_tests || !other_options.build_twizzler { + return Ok(None); + } + crate::toolchain::set_dynamic(); + crate::toolchain::set_cc(); + crate::print_status_line("collection: userspace::tests", Some(build_config)); + let triple = Triple::new( + build_config.arch, + build_config.machine, + crate::triple::Host::Twizzler, + None, + ); + let mut packages = locate_packages(workspace, None); + let mut options = CompileOptions::new(workspace.gctx(), mode)?; + options.build_config = + BuildConfig::new(workspace.gctx(), None, false, &[triple.to_string()], mode)?; + options.build_config.message_format = other_options.message_format; + if build_config.profile == Profile::Release { + options.build_config.requested_profile = InternedString::new("release"); + } + // TODO: once we have switched to the new runtime, all these should be removed. options.spec = Packages::Packages( packages .iter() @@ -302,6 +366,10 @@ fn maybe_build_tests<'a>( "twizzler-futures" => None, "twizzler-async" => None, "twizzler-queue" => None, + "twizzler-queue-raw" => None, + "secgate" => None, + "twizzler-driver" => None, + "twizzler-abi" => None, _ => Some(p.name().to_string()), }) .collect(), @@ -436,7 +504,10 @@ pub(crate) struct TwizzlerCompilation { pub user_compilation: Option>, #[borrows(static_workspace, static_compilation)] #[covariant] - pub test_compilation: Option>, + pub static_test_compilation: Option>, + #[borrows(user_workspace, user_compilation)] + #[covariant] + pub user_test_compilation: Option>, #[borrows(kernel_workspace)] #[covariant] pub test_kernel_compilation: Option>, @@ -527,7 +598,8 @@ fn compile( |w| build_kernel(w, mode, &bc, other_options), |w| build_static(w, mode, &bc, other_options), |w| build_twizzler(w, mode, &bc, other_options), - |w, sc| maybe_build_tests(w, &bc, sc, other_options), + |w, sc| maybe_build_tests_static(w, &bc, sc, other_options), + |w, uc| maybe_build_tests_dynamic(w, &bc, uc, other_options), |w| maybe_build_kernel_tests(w, &bc, other_options), |w| build_third_party(w, mode, &bc, other_options), ) diff --git a/tools/xtask/src/image.rs b/tools/xtask/src/image.rs index 8af03327..b4f3fcaf 100644 --- a/tools/xtask/src/image.rs +++ b/tools/xtask/src/image.rs @@ -183,7 +183,8 @@ fn build_initrd(cli: &ImageOptions, comp: &TwizzlerCompilation) -> anyhow::Resul } } - if let Some(ref test_comp) = comp.borrow_test_compilation() { + // all the tests for init to run. + if let Some(ref test_comp) = comp.borrow_static_test_compilation() { let mut testlist = String::new(); for bin in test_comp.tests.iter() { initrd_files.push(bin.path.clone()); @@ -206,7 +207,54 @@ fn build_initrd(cli: &ImageOptions, comp: &TwizzlerCompilation) -> anyhow::Resul assert!(!cli.tests && !cli.benches); } + // all the tests for the monitor to run. Eventually this and the above will merge into + // one thing, but that will have to wait until the dynamic runtime is default. + if let Some(ref test_comp) = comp.borrow_user_test_compilation() { + let mut testlist = String::new(); + for bin in test_comp.tests.iter() { + initrd_files.push(bin.path.clone()); + testlist += &bin.path.file_name().unwrap().to_string_lossy(); + testlist += "\n"; + } + if cli.tests { + let test_file_path = get_genfile_path(comp, "monitor_test_bins"); + let mut file = File::create(&test_file_path)?; + file.write_all(testlist.as_bytes())?; + initrd_files.push(test_file_path); + } + } else { + assert!(!cli.tests && !cli.benches); + } + + let montest = comp + .borrow_user_test_compilation() + .as_ref() + .and_then(|utc| { + utc.tests + .iter() + .find_map(|p| { + if p.unit.pkg.name() == "montest" { + Some(p.path.file_name().unwrap().to_string_lossy().to_owned()) + } else { + None + } + }) + .clone() + }); + + if let Some(montest) = montest { + let test_file_path = get_genfile_path(comp, "monitor_test_info"); + let mut file = File::create(&test_file_path)?; + file.write_all(montest.as_bytes())?; + initrd_files.push(test_file_path); + } + let mut lib_path = crate::toolchain::get_rustlib_lib(&cli.config.twz_triple().to_string())?; + let mut testso_path = crate::toolchain::get_rust_stage2_std( + guess_host_triple::guess_host_triple().unwrap(), + &cli.config.twz_triple().to_string(), + )?; + testso_path.push("libtest.so"); let libstd_name = walkdir::WalkDir::new(lib_path.clone()) .min_depth(1) .max_depth(2) @@ -228,6 +276,7 @@ fn build_initrd(cli: &ImageOptions, comp: &TwizzlerCompilation) -> anyhow::Resul .to_owned(); lib_path.push(libstd_name); + //initrd_files.push(testso_path); initrd_files.push(lib_path); Ok(initrd_files) diff --git a/tools/xtask/src/toolchain.rs b/tools/xtask/src/toolchain.rs index c3ab58e2..0ac05987 100644 --- a/tools/xtask/src/toolchain.rs +++ b/tools/xtask/src/toolchain.rs @@ -289,7 +289,7 @@ pub(crate) fn do_bootstrap(cli: BootstrapOptions) -> anyhow::Result<()> { pub fn set_dynamic() { std::env::set_var( "RUSTFLAGS", - "-C prefer-dynamic=y -Z staticlib-prefer-dynamic=y", + "-C prefer-dynamic=y -Z staticlib-prefer-dynamic=y -C link-arg=--allow-shlib-undefined", ); std::env::set_var("CARGO_TARGET_DIR", "target/dynamic"); } @@ -411,6 +411,17 @@ pub fn get_rust_lld(host_triple: &str) -> anyhow::Result { Ok(rustlib_bin) } +pub fn get_rust_stage2_std(host_triple: &str, target_triple: &str) -> anyhow::Result { + let curdir = std::env::current_dir().unwrap(); + let dir = curdir + .join("toolchain/src/rust/build") + .join(host_triple) + .join("stage2-std") + .join(target_triple) + .join("release"); + Ok(dir) +} + fn generate_config_toml() -> anyhow::Result<()> { /* We need to add two(ish) things to the config.toml for rustc: the paths of tools for each twizzler target (built by LLVM as part of rustc), and the host triple (added to the list of triples to support). */