blob: 705f54b1cb71b81e256855aec5088f9705764a4b [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
failure::{Error, ResultExt},
fidl_fuchsia_hardware_tee::DeviceConnectorProxy,
fuchsia_async as fasync,
fuchsia_syslog::fx_log_err,
fuchsia_zircon as zx,
fuchsia_zircon::AsHandleRef,
futures::{future::AbortHandle, lock::Mutex, prelude::*},
std::{fs::File, path::Path, sync::Arc},
};
struct AbortContext {
pub is_aborted: bool,
pub abort_handles: Vec<AbortHandle>,
}
impl AbortContext {
pub fn new() -> Self {
Self { is_aborted: false, abort_handles: Vec::new() }
}
}
/// `TeeDeviceConnection` represents a connection to a TEE device driver serving
/// fuchsia.hardware.tee.DeviceConnector.
#[derive(Clone, Debug)]
pub struct TeeDeviceConnection {
pub connector_proxy: DeviceConnectorProxy,
abort_handles_lock: Arc<Mutex<AbortContext>>,
}
impl TeeDeviceConnection {
pub fn create(path: impl AsRef<Path>) -> Result<Self, Error> {
let file = File::open(path).context("Failed to open TEE device in devfs")?;
let devfs_zx_chan = zx::Channel::from(
fdio::transfer_fd(file).context("Could not convert devfs file descriptor to handle")?,
);
// Create a Rust async channel from the zx::Channel
let devfs_chan = fasync::Channel::from_channel(devfs_zx_chan)?;
// Convert the channel to a FIDL connection for the ManagerConnector
let connector_proxy = DeviceConnectorProxy::new(devfs_chan);
// Start a future that invokes registered AbortHandles upon device connection closing
let abort_handles_lock = Arc::new(Mutex::new(AbortContext::new()));
let abort_handles_lock_cloned = abort_handles_lock.clone();
let on_closed_fut = fasync::OnSignals::new(
&connector_proxy.as_handle_ref(),
zx::Signals::CHANNEL_PEER_CLOSED,
)
.map(|res| res.map(|_| ()))
.unwrap_or_else(|e| fx_log_err!("{:?}", e));
fasync::spawn(async move {
let abort_handles_lock = abort_handles_lock_cloned;
await!(on_closed_fut);
let abort_context = &mut *await!(abort_handles_lock.lock());
abort_context.is_aborted = true;
for handle in &abort_context.abort_handles {
handle.abort();
}
abort_context.abort_handles.clear();
});
Ok(Self { connector_proxy, abort_handles_lock })
}
/// Register an `AbortHandle` to be invoked when the device connection closes.
pub async fn register_abort_handle_on_closed(&self, handle: AbortHandle) {
let abort_context = &mut *await!(self.abort_handles_lock.lock());
if abort_context.is_aborted {
// Since the device closed event has already been received, immediately close the handle
handle.abort();
}
abort_context.abort_handles.push(handle);
}
}