| // 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(()) |
| } |
| } |