use crate::key::{Hierarchy, Key, KeyAlg};
use crate::{check_rc, sys, Result, TpmError, E_DEVICE_IN_USE};
use alloc::boxed::Box;
use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicBool, Ordering};
static DEVICE_ACTIVE: AtomicBool = AtomicBool::new(false);
pub struct Device {
dev: Box<UnsafeCell<sys::WOLFTPM2_DEV>>,
}
impl Device {
#[cfg(any(swtpm, devtpm, mmio, winapi, linux_autodetect))]
pub fn open() -> Result<Self> {
Self::init_with(None)
}
pub unsafe fn open_with_io_cb(io_cb: sys::TPM2HalIoCb) -> Result<Self> {
Self::init_with(io_cb)
}
#[cfg(swtpm)]
pub fn open_swtpm() -> Result<Self> {
Self::init_with(None)
}
fn acquire() -> Result<()> {
if DEVICE_ACTIVE
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
return Err(TpmError(E_DEVICE_IN_USE));
}
Ok(())
}
fn init_with(io_cb: sys::TPM2HalIoCb) -> Result<Self> {
Self::acquire()?;
Self::init_held(io_cb)
}
fn init_held(io_cb: sys::TPM2HalIoCb) -> Result<Self> {
let dev: Box<UnsafeCell<sys::WOLFTPM2_DEV>> =
Box::new(UnsafeCell::new(unsafe { core::mem::zeroed() }));
let rc = unsafe { sys::wolfTPM2_Init(dev.get(), io_cb, core::ptr::null_mut()) };
if rc != 0 {
unsafe { sys::wolfTPM2_Cleanup(dev.get()) };
DEVICE_ACTIVE.store(false, Ordering::Release);
return Err(TpmError(rc));
}
Ok(Device { dev })
}
pub(crate) fn ptr(&self) -> *mut sys::WOLFTPM2_DEV {
self.dev.get()
}
#[cfg(rng)]
pub fn get_random(&self, buf: &mut [u8]) -> Result<()> {
if buf.is_empty() {
return Ok(());
}
let n = crate::checked_u32(buf.len())?;
let rc = unsafe { sys::wolfTPM2_GetRandom(self.ptr(), buf.as_mut_ptr(), n) };
check_rc(rc)
}
pub fn create_primary(
&self,
hierarchy: Hierarchy,
alg: KeyAlg,
auth: Option<&[u8]>,
) -> Result<Key<'_>> {
Key::create_primary(self, hierarchy, alg, auth)
}
}
impl Drop for Device {
fn drop(&mut self) {
unsafe { sys::wolfTPM2_Cleanup(self.dev.get()) };
DEVICE_ACTIVE.store(false, Ordering::Release);
}
}