blob: 2582279d08889e10cb2dbbe6c1a559f4e6e07a92 [file] [log] [blame]
// Copyright 2020 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 {
crate::{builtin::capability::ResourceCapability, capability::*},
anyhow::Error,
async_trait::async_trait,
cm_rust::CapabilityName,
fidl::endpoints::ServiceMarker,
fidl_fuchsia_kernel as fkernel,
fuchsia_zircon::{self as zx, HandleBased, Resource, ResourceInfo},
futures::prelude::*,
lazy_static::lazy_static,
std::sync::Arc,
};
lazy_static! {
static ref IOPORT_RESOURCE_CAPABILITY_NAME: CapabilityName =
"fuchsia.kernel.IoportResource".into();
}
/// An implementation of fuchsia.kernel.IoportResource protocol.
pub struct IoportResource {
resource: Resource,
}
#[cfg(target_arch = "x86_64")]
impl IoportResource {
/// `resource` must be the IOPORT resource.
pub fn new(resource: Resource) -> Arc<Self> {
Arc::new(Self { resource })
}
}
#[async_trait]
impl ResourceCapability for IoportResource {
const KIND: zx::sys::zx_rsrc_kind_t = zx::sys::ZX_RSRC_KIND_IOPORT;
const NAME: &'static str = "IoportResource";
type Marker = fkernel::IoportResourceMarker;
fn get_resource_info(self: &Arc<Self>) -> Result<ResourceInfo, Error> {
Ok(self.resource.info()?)
}
async fn server_loop(
self: Arc<Self>,
mut stream: <Self::Marker as ServiceMarker>::RequestStream,
) -> Result<(), Error> {
while let Some(fkernel::IoportResourceRequest::Get { responder }) =
stream.try_next().await?
{
responder.send(self.resource.duplicate_handle(zx::Rights::SAME_RIGHTS)?)?;
}
Ok(())
}
fn matches_routed_capability(&self, capability: &InternalCapability) -> bool {
capability.matches_protocol(&IOPORT_RESOURCE_CAPABILITY_NAME)
}
}
#[cfg(all(test, target_arch = "x86_64"))]
mod tests {
use {
super::*,
crate::{
builtin::capability::BuiltinCapability,
model::hooks::{Event, EventPayload, Hooks},
},
fidl::endpoints::ClientEnd,
fidl_fuchsia_kernel as fkernel, fuchsia_async as fasync,
fuchsia_component::client::connect_to_service,
fuchsia_zircon::AsHandleRef,
fuchsia_zircon_sys as sys,
futures::lock::Mutex,
moniker::AbsoluteMoniker,
std::path::PathBuf,
};
fn ioport_resource_available() -> bool {
let bin = std::env::args().next();
match bin.as_ref().map(String::as_ref) {
Some("/pkg/bin/component_manager_test") => false,
Some("/pkg/bin/component_manager_boot_env_test") => true,
_ => panic!("Unexpected test binary name {:?}", bin),
}
}
async fn get_ioport_resource() -> Result<Resource, Error> {
let ioport_resource_provider = connect_to_service::<fkernel::IoportResourceMarker>()?;
let ioport_resource_handle = ioport_resource_provider.get().await?;
Ok(Resource::from(ioport_resource_handle))
}
async fn serve_ioport_resource() -> Result<fkernel::IoportResourceProxy, Error> {
let ioport_resource = get_ioport_resource().await?;
let (proxy, stream) =
fidl::endpoints::create_proxy_and_stream::<fkernel::IoportResourceMarker>()?;
fasync::Task::local(
IoportResource::new(ioport_resource)
.serve(stream)
.unwrap_or_else(|e| panic!("Error while serving IOPORT resource service: {}", e)),
)
.detach();
Ok(proxy)
}
#[fasync::run_singlethreaded(test)]
async fn fail_with_no_ioport_resource() -> Result<(), Error> {
if ioport_resource_available() {
return Ok(());
}
let (_, stream) =
fidl::endpoints::create_proxy_and_stream::<fkernel::IoportResourceMarker>()?;
assert!(!IoportResource::new(Resource::from(zx::Handle::invalid()))
.serve(stream)
.await
.is_ok());
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn kind_type_is_ioport() -> Result<(), Error> {
if !ioport_resource_available() {
return Ok(());
}
let ioport_resource_provider = serve_ioport_resource().await?;
let ioport_resource: Resource = ioport_resource_provider.get().await?;
let resource_info = ioport_resource.info()?;
assert_eq!(resource_info.kind, zx::sys::ZX_RSRC_KIND_IOPORT);
assert_eq!(resource_info.base, 0);
assert_eq!(resource_info.size, 0);
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn can_connect_to_ioport_service() -> Result<(), Error> {
if !ioport_resource_available() {
return Ok(());
}
let ioport_resource = IoportResource::new(get_ioport_resource().await?);
let hooks = Hooks::new(None);
hooks.install(ioport_resource.hooks()).await;
let provider = Arc::new(Mutex::new(None));
let source = CapabilitySource::Builtin {
capability: InternalCapability::Protocol(IOPORT_RESOURCE_CAPABILITY_NAME.clone()),
};
let event = Event::new_for_test(
AbsoluteMoniker::root(),
"fuchsia-pkg://root",
Ok(EventPayload::CapabilityRouted { source, capability_provider: provider.clone() }),
);
hooks.dispatch(&event).await?;
let (client, mut server) = zx::Channel::create()?;
if let Some(provider) = provider.lock().await.take() {
provider.open(0, 0, PathBuf::new(), &mut server).await?;
}
let ioport_client = ClientEnd::<fkernel::IoportResourceMarker>::new(client)
.into_proxy()
.expect("failed to create launcher proxy");
let ioport_resource = ioport_client.get().await?;
assert_ne!(ioport_resource.raw_handle(), sys::ZX_HANDLE_INVALID);
Ok(())
}
}