blob: e89e81829a7480aa3e525b2aac1beacfa6cd9724 [file] [log] [blame]
// Copyright 2021 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 {
anyhow::{Context, Error, Result},
fidl::endpoints::DiscoverableProtocolMarker,
fidl_fuchsia_boot as fboot, fidl_fuchsia_driver_development as fdd,
fidl_fuchsia_driver_framework as fdf, fidl_fuchsia_driver_test as fdt, fidl_fuchsia_io as fio,
fuchsia_async as fasync,
fuchsia_component_test::{
Capability, ChildOptions, ChildRef, LocalComponentHandles, RealmBuilder, Route,
},
fuchsia_driver_test::{DriverTestRealmBuilder, DriverTestRealmInstance},
fuchsia_zircon as zx,
futures::{FutureExt as _, StreamExt as _},
vfs::{
directory::entry_container::Directory, execution_scope::ExecutionScope, path::Path, service,
},
};
async fn get_driver_info(
service: &fdd::ManagerProxy,
driver_filter: &[String],
) -> Result<Vec<fdf::DriverInfo>> {
let (iterator, iterator_server) =
fidl::endpoints::create_proxy::<fdd::DriverInfoIteratorMarker>()?;
service
.get_driver_info(driver_filter, iterator_server)
.context("FIDL call to get driver info failed")?;
let mut info_result = Vec::new();
loop {
let mut driver_info =
iterator.get_next().await.context("FIDL call to get driver info failed")?;
if driver_info.len() == 0 {
break;
}
info_result.append(&mut driver_info)
}
Ok(info_result)
}
async fn serve_boot_items(handles: LocalComponentHandles) -> Result<(), Error> {
let export = vfs::pseudo_directory! {
"svc" => vfs::pseudo_directory! {
fboot::ItemsMarker::PROTOCOL_NAME => service::host(move |stream| {
run_boot_items(stream)
}),
},
};
let scope = ExecutionScope::new();
export.open(
scope.clone(),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::DIRECTORY,
Path::dot(),
fidl::endpoints::ServerEnd::from(handles.outgoing_dir.into_channel()),
);
scope.wait().await;
Ok(())
}
async fn run_boot_items(mut stream: fboot::ItemsRequestStream) {
/// This constant is defined in
/// sdk/lib/zbi-format/include/lib/zbi-format/zbi.h.
const ZBI_TYPE_PLATFORM_ID: u32 = 0x44494c50;
/// These constants are defined in
/// zircon/system/ulib/ddk-platform-defs/include/lib/ddk/platform-defs.h
const PDEV_VID_TEST: u32 = 0x11;
const PDEV_PID_PBUS_TEST: u32 = 0x01;
/// This struct is defined in sdk/lib/zbi-format/include/lib/zbi-format/board.h
struct ZbiPlatformId {
_vid: u32,
_pid: u32,
_board_name: [u8; 32],
}
while let Some(request) = stream.next().await {
match request.unwrap() {
fboot::ItemsRequest::Get { type_, extra: _, responder } => {
if type_ == ZBI_TYPE_PLATFORM_ID {
let platform_id = ZbiPlatformId {
_vid: PDEV_VID_TEST,
_pid: PDEV_PID_PBUS_TEST,
_board_name: [0; 32],
};
const PLATFORM_ID_SIZE: usize = std::mem::size_of::<ZbiPlatformId>();
let vmo = zx::Vmo::create(PLATFORM_ID_SIZE as u64).unwrap();
let bytes = unsafe {
std::mem::transmute::<ZbiPlatformId, [u8; PLATFORM_ID_SIZE]>(platform_id)
};
vmo.write(&bytes, PLATFORM_ID_SIZE as u64).unwrap();
responder.send(Some(vmo), PLATFORM_ID_SIZE as u32).unwrap();
} else {
responder.send(None, 0).unwrap();
}
}
fboot::ItemsRequest::Get2 { responder, type_: _, extra: _ } => {
responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw())).unwrap();
}
fboot::ItemsRequest::GetBootloaderFile { responder, filename: _ } => {
responder.send(None).unwrap();
}
}
}
}
#[fasync::run_singlethreaded(test)]
async fn test_smoke_test() -> Result<()> {
let realm = RealmBuilder::new().await?;
realm.driver_test_realm_setup().await?;
let instance = realm.build().await?;
instance.driver_test_realm_start(fdt::RealmArgs::default()).await?;
// Connect to a protocol to ensure that it starts, then immediately exit.
let _ = instance.root.connect_to_protocol_at_exposed_dir::<fdd::ManagerMarker>()?;
Ok(())
}
// Run DriverTestRealm with no arguments and see that the drivers in our package
// are loaded.
#[fasync::run_singlethreaded(test)]
async fn test_empty_args() -> Result<()> {
let realm = RealmBuilder::new().await?;
realm.driver_test_realm_setup().await?;
let instance = realm.build().await?;
instance.driver_test_realm_start(fdt::RealmArgs::default()).await?;
let driver_dev = instance.root.connect_to_protocol_at_exposed_dir::<fdd::ManagerMarker>()?;
let info = get_driver_info(&driver_dev, &[]).await?;
assert!(info
.iter()
.any(|d| d.url == Some("fuchsia-boot:///dtr#meta/test-parent-sys.cm".to_string())));
assert!(info.iter().any(|d| d.url == Some("fuchsia-boot:///dtr#meta/test.cm".to_string())));
Ok(())
}
// Manually open our /pkg directory and pass it to DriverTestRealm to see that it works.
#[fasync::run_singlethreaded(test)]
async fn test_pkg_dir() -> Result<()> {
let realm = RealmBuilder::new().await?;
realm.driver_test_realm_setup().await?;
let instance = realm.build().await?;
let (pkg, pkg_server) = fidl::endpoints::create_endpoints::<fio::DirectoryMarker>();
let (boot, boot_server) = fidl::endpoints::create_endpoints::<fio::DirectoryMarker>();
let pkg_flags = fuchsia_fs::OpenFlags::RIGHT_READABLE
| fuchsia_fs::OpenFlags::RIGHT_EXECUTABLE
| fio::OpenFlags::DIRECTORY;
fuchsia_fs::directory::open_channel_in_namespace("/pkg", pkg_flags, boot_server).unwrap();
// We send a bogus directory into pkg in order ensure we don't double index the same driver.
fuchsia_fs::directory::open_channel_in_namespace("/pkg/bin", pkg_flags, pkg_server).unwrap();
let args = fdt::RealmArgs { boot: Some(boot), pkg: Some(pkg), ..Default::default() };
instance.driver_test_realm_start(args).await?;
let driver_dev = instance.root.connect_to_protocol_at_exposed_dir::<fdd::ManagerMarker>()?;
let info = get_driver_info(&driver_dev, &[]).await?;
assert!(info
.iter()
.any(|d| d.url == Some("fuchsia-boot:///dtr#meta/test-parent-sys.cm".to_string())));
assert!(info.iter().any(|d| d.url == Some("fuchsia-boot:///dtr#meta/test.cm".to_string())));
let dev = instance.driver_test_realm_connect_to_dev()?;
device_watcher::recursive_wait(&dev, "sys/test/test").await?;
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_root_driver() -> Result<()> {
let realm = RealmBuilder::new().await?;
realm.driver_test_realm_setup().await?;
let instance = realm.build().await?;
let args = fdt::RealmArgs {
root_driver: Some("fuchsia-boot:///platform-bus#meta/platform-bus.cm".to_string()),
..Default::default()
};
instance.driver_test_realm_start(args).await?;
let dev = instance.driver_test_realm_connect_to_dev()?;
device_watcher::recursive_wait(&dev, "sys/platform").await?;
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_tunnel_boot_items() -> Result<()> {
let realm = RealmBuilder::new().await?;
realm.driver_test_realm_setup().await?;
let boot_items = realm
.add_local_child("boot_items", move |h| serve_boot_items(h).boxed(), ChildOptions::new())
.await?;
let driver_test_realm: ChildRef = fuchsia_driver_test::COMPONENT_NAME.into();
realm
.add_route(
Route::new()
.capability(Capability::protocol::<fboot::ItemsMarker>())
.from(&boot_items)
.to(&driver_test_realm),
)
.await?;
realm.init_mutable_config_from_package(&driver_test_realm).await?;
realm.set_config_value(&driver_test_realm, "tunnel_boot_items", true.into()).await?;
let instance = realm.build().await?;
let args = fdt::RealmArgs {
root_driver: Some("fuchsia-boot:///platform-bus#meta/platform-bus.cm".to_string()),
..Default::default()
};
instance.driver_test_realm_start(args).await?;
let dev = instance.driver_test_realm_connect_to_dev()?;
device_watcher::recursive_wait(&dev, "sys/platform").await?;
Ok(())
}