use std::ffi::CString;
use std::path::Path;
use libloading::{Library, Symbol};
use vj_abi::{EvalResult, Region, SimHandle, SimTime, SlotId};
#[derive(Debug)]
pub enum LoaderError {
LoadFailed(String),
SymbolNotFound(&'static str),
AbiVersionMismatch { expected: u32, found: u32 },
}
impl std::fmt::Display for LoaderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LoaderError::LoadFailed(msg) => write!(f, "failed to load kernel: {msg}"),
LoaderError::SymbolNotFound(sym) => {
write!(f, "kernel is missing required ABI symbol {sym:?}")
}
LoaderError::AbiVersionMismatch { expected, found } => write!(
f,
"kernel ABI version mismatch: this loader expects VJ_ABI_VERSION={expected}, \
kernel's sim_abi_version() reported {found} -- refusing to load (never calling \
any other entry point on a version-mismatched kernel)"
),
}
}
}
impl std::error::Error for LoaderError {}
pub struct LoadedKernel {
_lib: Library,
sim_init: vj_abi::SimInitFn,
sim_settle: vj_abi::SimSettleFn,
sim_eval_region: vj_abi::SimEvalRegionFn,
sim_commit_nba: vj_abi::SimCommitNbaFn,
sim_step_time: vj_abi::SimStepTimeFn,
sim_final: vj_abi::SimFinalFn,
sim_process_count: vj_abi::SimProcessCountFn,
sv_get_scalar: vj_abi::SvGetScalarFn,
sv_set_scalar: vj_abi::SvSetScalarFn,
}
fn nul_terminated(name: &'static str) -> CString {
CString::new(name).unwrap_or_else(|e| {
panic!("ABI symbol name constant {name:?} contains an interior NUL: {e}")
})
}
unsafe fn resolve<T: Copy>(lib: &Library, name: &'static str) -> Result<T, LoaderError> {
let cname = nul_terminated(name);
let sym: Symbol<'_, T> = unsafe { lib.get(cname.as_bytes_with_nul()) }
.map_err(|_| LoaderError::SymbolNotFound(name))?;
Ok(*sym)
}
impl LoadedKernel {
pub fn load_dynamic(path: &Path) -> Result<Self, LoaderError> {
let lib = unsafe { Library::new(path) }
.map_err(|e| LoaderError::LoadFailed(format!("{}: {e}", path.display())))?;
let sim_abi_version: vj_abi::SimAbiVersionFn =
unsafe { resolve(&lib, vj_abi::version::SIM_ABI_VERSION_SYMBOL) }?;
let found = sim_abi_version();
if found != vj_abi::VJ_ABI_VERSION {
return Err(LoaderError::AbiVersionMismatch {
expected: vj_abi::VJ_ABI_VERSION,
found,
});
}
let sim_init: vj_abi::SimInitFn = unsafe { resolve(&lib, vj_abi::entry::SIM_INIT_SYMBOL) }?;
let sim_settle: vj_abi::SimSettleFn =
unsafe { resolve(&lib, vj_abi::entry::SIM_SETTLE_SYMBOL) }?;
let sim_eval_region: vj_abi::SimEvalRegionFn =
unsafe { resolve(&lib, vj_abi::entry::SIM_EVAL_REGION_SYMBOL) }?;
let sim_commit_nba: vj_abi::SimCommitNbaFn =
unsafe { resolve(&lib, vj_abi::entry::SIM_COMMIT_NBA_SYMBOL) }?;
let sim_step_time: vj_abi::SimStepTimeFn =
unsafe { resolve(&lib, vj_abi::entry::SIM_STEP_TIME_SYMBOL) }?;
let sim_final: vj_abi::SimFinalFn =
unsafe { resolve(&lib, vj_abi::entry::SIM_FINAL_SYMBOL) }?;
let sim_process_count: vj_abi::SimProcessCountFn =
unsafe { resolve(&lib, vj_abi::entry::SIM_PROCESS_COUNT_SYMBOL) }?;
let sv_get_scalar: vj_abi::SvGetScalarFn =
unsafe { resolve(&lib, vj_abi::accessor::SV_GET_SCALAR_SYMBOL) }?;
let sv_set_scalar: vj_abi::SvSetScalarFn =
unsafe { resolve(&lib, vj_abi::accessor::SV_SET_SCALAR_SYMBOL) }?;
Ok(LoadedKernel {
_lib: lib,
sim_init,
sim_settle,
sim_eval_region,
sim_commit_nba,
sim_step_time,
sim_final,
sim_process_count,
sv_get_scalar,
sv_set_scalar,
})
}
pub fn sim_init(&mut self, design_id: u32) -> SimHandle {
(self.sim_init)(design_id)
}
pub fn sim_settle(&mut self, h: SimHandle) -> EvalResult {
(self.sim_settle)(h)
}
pub fn sim_eval_region(&mut self, h: SimHandle, region: Region, t: SimTime) -> EvalResult {
(self.sim_eval_region)(h, region, t)
}
pub fn sim_commit_nba(&mut self, h: SimHandle, t: SimTime) -> EvalResult {
(self.sim_commit_nba)(h, t)
}
pub fn sim_step_time(&mut self, h: SimHandle, t: SimTime) -> EvalResult {
(self.sim_step_time)(h, t)
}
pub fn sim_final(&mut self, h: SimHandle) {
(self.sim_final)(h)
}
pub fn sim_process_count(&mut self, h: SimHandle) -> u32 {
(self.sim_process_count)(h)
}
pub fn sv_get_scalar(&mut self, h: SimHandle, slot: SlotId) -> u128 {
(self.sv_get_scalar)(h, slot)
}
pub fn sv_set_scalar(&mut self, h: SimHandle, slot: SlotId, v: u128) {
(self.sv_set_scalar)(h, slot, v)
}
}