| // 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/device/llcpp/fidl.h> |
| #include <fuchsia/device/test/llcpp/fidl.h> |
| #include <fuchsia/driver/development/cpp/fidl.h> |
| #include <lib/ddk/binding.h> |
| #include <lib/ddk/driver.h> |
| #include <lib/devmgr-integration-test/fixture.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/sys/cpp/component_context.h> |
| |
| #include <gtest/gtest.h> |
| |
| #include "src/lib/fxl/strings/string_printf.h" |
| |
| const std::string kDevPrefix = "/dev/"; |
| const std::string kDriverTestDir = "/boot/driver/test"; |
| const std::string kDriverLibname = "string-bind-parent.so"; |
| const std::string kDriverLibPath = kDriverTestDir + "/" + kDriverLibname; |
| const std::string kStringBindDriverLibPath = kDriverTestDir + "/string-bind-child.so"; |
| const std::string kChildDeviceName = "child"; |
| |
| using devmgr_integration_test::IsolatedDevmgr; |
| |
| class StringBindTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| auto args = IsolatedDevmgr::DefaultArgs(); |
| |
| args.driver_search_paths.push_back("/boot/driver"); |
| |
| ASSERT_EQ(IsolatedDevmgr::Create(std::move(args), &devmgr_), ZX_OK); |
| ASSERT_NE(devmgr_.svc_root_dir().channel(), ZX_HANDLE_INVALID); |
| |
| // Wait for /dev/test/test to appear, then get a channel to it. |
| fbl::unique_fd root_fd; |
| ASSERT_EQ(ZX_OK, devmgr_integration_test::RecursiveWaitForFile(devmgr_.devfs_root(), |
| "test/test", &root_fd)); |
| |
| auto root_device_endpoints = fidl::CreateEndpoints<fuchsia_device_test::RootDevice>(); |
| ASSERT_EQ(ZX_OK, root_device_endpoints.status_value()); |
| |
| auto root_device = fidl::BindSyncClient(std::move(root_device_endpoints->client)); |
| auto status = fdio_get_service_handle(root_fd.release(), |
| root_device.mutable_channel()->reset_and_get_address()); |
| ASSERT_EQ(ZX_OK, status); |
| |
| auto endpoints = fidl::CreateEndpoints<fuchsia_device::Controller>(); |
| ASSERT_EQ(ZX_OK, endpoints.status_value()); |
| |
| // Create the root test device in /dev/test/test, and get its relative path from /dev. |
| auto result = root_device.CreateDevice(fidl::StringView::FromExternal(kDriverLibname), |
| endpoints->server.TakeChannel()); |
| |
| ASSERT_EQ(ZX_OK, result.status()); |
| |
| ASSERT_GE(result->path.size(), kDevPrefix.size()); |
| ASSERT_EQ(strncmp(result->path.data(), kDevPrefix.c_str(), kDevPrefix.size()), 0); |
| relative_device_path_ = std::string(result->path.data() + kDevPrefix.size(), |
| result->path.size() - kDevPrefix.size()); |
| |
| // Bind the test driver to the new device. |
| auto response = |
| fidl::WireCall(endpoints->client).Bind(::fidl::StringView::FromExternal(kDriverLibPath)); |
| status = response.status(); |
| if (status == ZX_OK) { |
| if (response->result.is_err()) { |
| status = response->result.err(); |
| } |
| } |
| ASSERT_EQ(ZX_OK, status); |
| |
| // Wait for the child device to bind and appear. The child device should bind with its string |
| // properties. |
| fbl::unique_fd string_bind_fd; |
| status = devmgr_integration_test::RecursiveWaitForFile( |
| devmgr_.devfs_root(), "test/test/string-bind-parent/child", &string_bind_fd); |
| ASSERT_EQ(ZX_OK, status); |
| |
| // Connect to the DriverDevelopment service. |
| auto bind_endpoints = fidl::CreateEndpoints<fuchsia::driver::development::DriverDevelopment>(); |
| ASSERT_EQ(ZX_OK, bind_endpoints.status_value()); |
| |
| std::string svc_name = |
| fxl::StringPrintf("svc/%s", fuchsia::driver::development::DriverDevelopment::Name_); |
| sys::ServiceDirectory svc_dir(devmgr_.TakeSvcRootDir().TakeChannel()); |
| status = svc_dir.Connect(svc_name, bind_endpoints->server.TakeChannel()); |
| ASSERT_EQ(ZX_OK, status); |
| |
| driver_dev_.Bind(bind_endpoints->client.TakeChannel()); |
| } |
| |
| IsolatedDevmgr devmgr_; |
| fuchsia::driver::development::DriverDevelopmentSyncPtr driver_dev_; |
| std::string relative_device_path_; |
| }; |
| |
| // Get the bind program of the test driver and check that it has the expected instructions. |
| TEST_F(StringBindTest, DriverBytecode) { |
| fuchsia::driver::development::DriverDevelopment_GetDriverInfo_Result result; |
| ASSERT_EQ(ZX_OK, driver_dev_->GetDriverInfo({kStringBindDriverLibPath}, &result)); |
| ASSERT_TRUE(result.is_response()); |
| ASSERT_EQ(result.response().drivers.size(), 1u); |
| auto bytecode = result.response().drivers[0].bind_rules().bytecode_v2(); |
| |
| const uint8_t kExpectedBytecode[] = { |
| 0x42, 0x49, 0x4E, 0x44, 0x02, 0x0, 0x0, 0x0, // Bind header |
| 0x53, 0x59, 0x4E, 0x42, 0x45, 0x00, 0x00, 0x00, // Symbol table header |
| 0x01, 0x0, 0x0, 0x0, // "stringbind.lib.kinglet" ID |
| 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x62, 0x69, 0x6e, 0x64, // "stringbind" |
| 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x6b, 0x69, 0x6e, 0x67, 0x6c, // ".lib.kingl" |
| 0x65, 0x74, 0x00, // "et" |
| 0x02, 0x00, 0x00, 0x00, // "firecrest" ID |
| 0x66, 0x69, 0x72, 0x65, 0x63, 0x72, 0x65, 0x73, 0x74, 0x00, // "firecrest" |
| 0x03, 0x00, 0x00, 0x00, // "stringbind.lib.bobolink" ID |
| 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x62, 0x69, 0x6e, 0x64, // "stringbind" |
| 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x62, 0x6f, 0x62, 0x6f, 0x6c, // ".lib.bobol" |
| 0x69, 0x6e, 0x6b, 0x00, // "ink" |
| 0x49, 0x4E, 0x53, 0x54, 0x21, 0x00, 0x00, 0x00, // Instruction header |
| 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, |
| 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, |
| 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x00, |
| }; |
| |
| ASSERT_EQ(countof(kExpectedBytecode), bytecode.size()); |
| for (size_t i = 0; i < bytecode.size(); i++) { |
| ASSERT_EQ(kExpectedBytecode[i], bytecode[i]); |
| } |
| } |
| |
| TEST_F(StringBindTest, DeviceProperties) { |
| std::string child_device_path(relative_device_path_ + "/" + kChildDeviceName); |
| |
| fuchsia::driver::development::DriverDevelopment_GetDeviceInfo_Result result; |
| ASSERT_EQ(ZX_OK, driver_dev_->GetDeviceInfo({child_device_path}, &result)); |
| |
| ASSERT_TRUE(result.is_response()); |
| |
| constexpr zx_device_prop_t kExpectedProps[] = { |
| {BIND_PROTOCOL, 0, 3}, |
| {BIND_PCI_VID, 0, 1234}, |
| {BIND_PCI_DID, 0, 1234}, |
| }; |
| |
| ASSERT_EQ(result.response().devices.size(), 1u); |
| auto props = result.response().devices[0].property_list().props; |
| ASSERT_EQ(props.size(), countof(kExpectedProps)); |
| for (size_t i = 0; i < props.size(); i++) { |
| ASSERT_EQ(props[i].id, kExpectedProps[i].id); |
| ASSERT_EQ(props[i].reserved, kExpectedProps[i].reserved); |
| ASSERT_EQ(props[i].value, kExpectedProps[i].value); |
| } |
| |
| auto& str_props = result.response().devices[0].property_list().str_props; |
| ASSERT_EQ(static_cast<size_t>(2), str_props.size()); |
| |
| ASSERT_STREQ("stringbind.lib.kinglet", str_props[0].key.data()); |
| ASSERT_TRUE(str_props[0].value.is_str_value()); |
| ASSERT_STREQ("firecrest", str_props[0].value.str_value().data()); |
| |
| ASSERT_STREQ("stringbind.lib.bobolink", str_props[1].key.data()); |
| ASSERT_TRUE(str_props[1].value.is_int_value()); |
| ASSERT_EQ(static_cast<uint32_t>(10), str_props[1].value.int_value()); |
| } |