blob: e3217094a54bc1a64f6ec3a3a9f65de62f57902c [file]
// 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());
}