blob: 4703914dd4e836534126258f24172a886be85af2 [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.
#include <fuchsia/driver/development/cpp/fidl.h>
#include <fuchsia/driver/test/cpp/fidl.h>
#include <lib/device-watcher/cpp/device-watcher.h>
#include <lib/driver_test_realm/realm_builder/cpp/lib.h>
#include <lib/sys/component/cpp/testing/realm_builder.h>
#include <fbl/unique_fd.h>
#include "lib/fdio/fd.h"
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
namespace {
class FidlProtocolTest : public gtest::TestLoopFixture {};
TEST_F(FidlProtocolTest, ChildBinds) {
auto realm_builder = component_testing::RealmBuilder::Create();
driver_test_realm::Setup(realm_builder);
auto realm = realm_builder.Build(dispatcher());
// Start DriverTestRealm.
fidl::SynchronousInterfacePtr<fuchsia::driver::test::Realm> driver_test_realm;
ASSERT_EQ(ZX_OK, realm.component().Connect(driver_test_realm.NewRequest()));
fuchsia::driver::test::Realm_Start_Result realm_result;
ASSERT_EQ(ZX_OK, driver_test_realm->Start(fuchsia::driver::test::RealmArgs(), &realm_result));
ASSERT_FALSE(realm_result.is_err());
// Connect to dev.
fidl::InterfaceHandle<fuchsia::io::Node> dev;
zx_status_t status = realm.component().exposed()->Open(fuchsia::io::OpenFlags::RIGHT_READABLE, {},
"dev", dev.NewRequest());
ASSERT_EQ(status, ZX_OK);
fbl::unique_fd root_fd;
status = fdio_fd_create(dev.TakeChannel().release(), root_fd.reset_and_get_address());
ASSERT_EQ(status, ZX_OK);
// Wait for the child device to bind and appear. The child driver should bind with its string
// properties. It will then make a call via FIDL and wait for the response before adding the child
// device.
ASSERT_EQ(
device_watcher::RecursiveWaitForFile(root_fd.get(), "sys/test/parent/child").status_value(),
ZX_OK);
// Wait for the other child device to bind to prevent a shutdown race condition bug.
ASSERT_EQ(
device_watcher::RecursiveWaitForFile(root_fd.get(), "sys/test/parent/child/isolated-child")
.status_value(),
ZX_OK);
}
TEST_F(FidlProtocolTest, ColocateFlagIsRespected) {
// Verify that the colocate flag set on isolated-child in BUILD.gn is respected by driver manager.
auto realm_builder = component_testing::RealmBuilder::Create();
driver_test_realm::Setup(realm_builder);
auto realm = realm_builder.Build(dispatcher());
// Start DriverTestRealm.
fidl::SynchronousInterfacePtr<fuchsia::driver::test::Realm> driver_test_realm;
ASSERT_EQ(ZX_OK, realm.component().Connect(driver_test_realm.NewRequest()));
fuchsia::driver::test::Realm_Start_Result realm_result;
ASSERT_EQ(ZX_OK, driver_test_realm->Start(fuchsia::driver::test::RealmArgs(), &realm_result));
ASSERT_FALSE(realm_result.is_err());
// Connect to dev.
fidl::InterfaceHandle<fuchsia::io::Node> dev;
zx_status_t status = realm.component().exposed()->Open(fuchsia::io::OpenFlags::RIGHT_READABLE, {},
"dev", dev.NewRequest());
ASSERT_EQ(status, ZX_OK);
fbl::unique_fd root_fd;
status = fdio_fd_create(dev.TakeChannel().release(), root_fd.reset_and_get_address());
ASSERT_EQ(status, ZX_OK);
// Wait for the device to bind and appear.
std::string parent_device_path = "sys/test/parent/child";
std::string device_path = parent_device_path + "/isolated-child";
zx::result channel = device_watcher::RecursiveWaitForFile(root_fd.get(), device_path.c_str());
ASSERT_EQ(channel.status_value(), ZX_OK);
// Connect to the driver development server.
fuchsia::driver::development::DriverDevelopmentSyncPtr driver_dev;
status = realm.component().Connect(driver_dev.NewRequest());
ASSERT_EQ(status, ZX_OK);
// Get the child device's driver host.
std::string device_path_full = "/dev/" + device_path;
fuchsia::driver::development::DeviceInfoIteratorSyncPtr iterator;
status =
driver_dev->GetDeviceInfo({device_path_full}, iterator.NewRequest(), /* exact_match= */ true);
ASSERT_EQ(status, ZX_OK);
std::vector<fuchsia::driver::development::DeviceInfo> child_device_result;
status = iterator->GetNext(&child_device_result);
ASSERT_EQ(status, ZX_OK);
ASSERT_EQ(child_device_result.size(), 1ull);
fuchsia::driver::development::DeviceInfo& device_info = child_device_result[0];
uint64_t child_driver_host_koid = device_info.driver_host_koid();
// Get the parent device's driver host.
std::string parent_device_path_full = "/dev/" + parent_device_path;
status = driver_dev->GetDeviceInfo({parent_device_path_full}, iterator.NewRequest(),
/* exact_match= */ true);
ASSERT_EQ(status, ZX_OK);
std::vector<fuchsia::driver::development::DeviceInfo> parent_device_result;
status = iterator->GetNext(&parent_device_result);
ASSERT_EQ(status, ZX_OK);
ASSERT_EQ(parent_device_result.size(), 1ull);
fuchsia::driver::development::DeviceInfo& parent_device_info = parent_device_result[0];
uint64_t parent_driver_host_koid = parent_device_info.driver_host_koid();
// Make sure the parent and child are in different hosts.
ASSERT_NE(child_driver_host_koid, parent_driver_host_koid);
}
TEST_F(FidlProtocolTest, MustIsolateFlagIsPassed) {
// Verify that the MUST_ISOLATE flag in the driver host is passed on to the driver manager.
//
// This is a regression test for fxb/112652.
auto realm_builder = component_testing::RealmBuilder::Create();
driver_test_realm::Setup(realm_builder);
auto realm = realm_builder.Build(dispatcher());
// Start DriverTestRealm.
fidl::SynchronousInterfacePtr<fuchsia::driver::test::Realm> driver_test_realm;
ASSERT_EQ(ZX_OK, realm.component().Connect(driver_test_realm.NewRequest()));
fuchsia::driver::test::Realm_Start_Result realm_result;
ASSERT_EQ(ZX_OK, driver_test_realm->Start(fuchsia::driver::test::RealmArgs(), &realm_result));
ASSERT_FALSE(realm_result.is_err());
// Connect to dev.
fidl::InterfaceHandle<fuchsia::io::Node> dev;
zx_status_t status = realm.component().exposed()->Open(fuchsia::io::OpenFlags::RIGHT_READABLE, {},
"dev", dev.NewRequest());
ASSERT_EQ(status, ZX_OK);
fbl::unique_fd root_fd;
status = fdio_fd_create(dev.TakeChannel().release(), root_fd.reset_and_get_address());
ASSERT_EQ(status, ZX_OK);
// Wait for the device to bind and appear.
std::string device_path = "sys/test/parent/child/isolated-child";
zx::result channel = device_watcher::RecursiveWaitForFile(root_fd.get(), device_path.c_str());
ASSERT_EQ(channel.status_value(), ZX_OK);
// Connect to the driver development server.
fuchsia::driver::development::DriverDevelopmentSyncPtr driver_dev;
status = realm.component().Connect(driver_dev.NewRequest());
ASSERT_EQ(status, ZX_OK);
fuchsia::driver::development::DeviceInfoIteratorSyncPtr iterator;
std::string device_path_full = "/dev/" + device_path;
status =
driver_dev->GetDeviceInfo({device_path_full}, iterator.NewRequest(), /* exact_match= */ true);
ASSERT_EQ(status, ZX_OK);
std::vector<fuchsia::driver::development::DeviceInfo> devices;
status = iterator->GetNext(&devices);
ASSERT_EQ(status, ZX_OK);
ASSERT_EQ(devices.size(), 1ull);
// Use the driver development FIDL to check that the created device has the MUST_ISOLATE flag set.
fuchsia::driver::development::DeviceInfo& device_info = devices[0];
ASSERT_TRUE(static_cast<bool>(device_info.flags() &
fuchsia::driver::development::DeviceFlags::MUST_ISOLATE));
}
TEST_F(FidlProtocolTest, ChildBindsV2) {
auto realm_builder = component_testing::RealmBuilder::Create();
driver_test_realm::Setup(realm_builder);
auto realm = realm_builder.Build(dispatcher());
// Start DriverTestRealm.
fidl::SynchronousInterfacePtr<fuchsia::driver::test::Realm> driver_test_realm;
ASSERT_EQ(ZX_OK, realm.component().Connect(driver_test_realm.NewRequest()));
fuchsia::driver::test::Realm_Start_Result realm_result;
auto args = fuchsia::driver::test::RealmArgs();
args.set_use_driver_framework_v2(true);
args.set_root_driver("fuchsia-boot:///#meta/test-parent-sys.cm");
ASSERT_EQ(ZX_OK, driver_test_realm->Start(std::move(args), &realm_result));
ASSERT_FALSE(realm_result.is_err());
// Connect to dev.
fidl::InterfaceHandle<fuchsia::io::Node> dev;
zx_status_t status = realm.component().exposed()->Open(fuchsia::io::OpenFlags::RIGHT_READABLE, {},
"dev", dev.NewRequest());
ASSERT_EQ(status, ZX_OK);
fbl::unique_fd root_fd;
status = fdio_fd_create(dev.TakeChannel().release(), root_fd.reset_and_get_address());
ASSERT_EQ(status, ZX_OK);
// Wait for the child device to bind and appear. The child driver should bind with its string
// properties. It will then make a call via FIDL and wait for the response before adding the child
// device.
ASSERT_EQ(
device_watcher::RecursiveWaitForFile(root_fd.get(), "sys/test/parent/child").status_value(),
ZX_OK);
// Wait for the other child device to bind to prevent a shutdown race condition bug.
ASSERT_EQ(
device_watcher::RecursiveWaitForFile(root_fd.get(), "sys/test/parent/child/isolated-child")
.status_value(),
ZX_OK);
}
} // namespace