#![no_std] //! Query a kernel before starting a user image that requires an ABI version. use turkos_os_syscall_abi::syscall_abi::{DecodeError, Errno}; #[cfg(all(target_arch = "aarch64", target_os = "none"))] use turkos_os_syscall_abi::syscall_abi::{AbiResult, ABI_VERSION, CALL_VERSION}; use turkos_os_user_runtime_abi::compatible; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Fault { Kernel(Errno), Wire(DecodeError), Unsupported { required: u64, available: u64 }, } pub trait VersionProbe { fn version(&mut self) -> Result; } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Session { required: u64, available: u64 } impl Session { pub const fn required(self) -> u64 { self.required } pub const fn available(self) -> u64 { self.available } pub fn supports(self, required: u64) -> bool { compatible(required, self.available).is_ok() } } /// One read-only version query precedes any caller-owned image acquisition. pub fn negotiate(probe: &mut P, required: u64) -> Result { let available = probe.version()?; compatible(required, available).map_err(|_| Fault::Unsupported { required, available, })?; Ok(Session {required, available}) } #[cfg(all(target_arch = "aarch64", target_os = "none"))] pub struct NativeProbe; #[cfg(all(target_arch = "aarch64", target_os = "none"))] impl VersionProbe for NativeProbe { fn version(&mut self) -> Result { let mut result = 0u64; unsafe { core::arch::asm!("svc #0", inout("x0") result, in("x7") ABI_VERSION, in("x8") CALL_VERSION, options(nostack)); } match AbiResult::decode(result).map_err(Fault::Wire)? { AbiResult::Ok(value) => Ok(value.value()), AbiResult::Err(error) => Err(Fault::Kernel(error)), } } }