| // 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 "src/devices/misc/drivers/compat/loader.h" |
| |
| #include <fidl/fuchsia.ldsvc/cpp/wire_test_base.h> |
| #include <lib/async-loop/cpp/loop.h> |
| |
| #include <gtest/gtest.h> |
| |
| #include "src/lib/testing/loop_fixture/test_loop_fixture.h" |
| |
| namespace fldsvc = fuchsia_ldsvc; |
| |
| namespace { |
| |
| zx_koid_t GetKoid(zx::vmo& vmo) { |
| zx_info_handle_basic_t info{}; |
| zx_status_t status = vmo.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr); |
| EXPECT_EQ(ZX_OK, status); |
| return info.koid; |
| } |
| |
| class TestEventHandler : public fidl::WireAsyncEventHandler<fldsvc::Loader> { |
| public: |
| fidl::Reason Reason() const { return error_.reason(); } |
| |
| private: |
| void on_fidl_error(fidl::UnbindInfo error) { error_ = error; } |
| |
| fidl::UnbindInfo error_; |
| }; |
| |
| class TestLoader : public fidl::testing::WireTestBase<fldsvc::Loader> { |
| public: |
| TestLoader() : vmo_() {} |
| explicit TestLoader(zx::vmo vmo) : vmo_(std::move(vmo)) {} |
| |
| private: |
| // fidl::WireServer<fuchsia_ldsvc::Loader> |
| void LoadObject(LoadObjectRequestView request, LoadObjectCompleter::Sync& completer) override { |
| completer.Reply(ZX_OK, std::move(vmo_)); |
| } |
| |
| void Config(ConfigRequestView request, ConfigCompleter::Sync& completer) override { |
| completer.Reply(ZX_OK); |
| } |
| |
| void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) override { |
| printf("Not implemented: Loader::%s\n", name.data()); |
| } |
| |
| zx::vmo vmo_; |
| }; |
| |
| } // namespace |
| |
| class LoaderTest : public gtest::TestLoopFixture {}; |
| |
| TEST_F(LoaderTest, LoadObject) { |
| auto endpoints = fidl::CreateEndpoints<fldsvc::Loader>(); |
| |
| // Create VMO for backing loader reply. |
| zx::vmo mylib_vmo; |
| zx_status_t status = zx::vmo::create(zx_system_get_page_size(), 0, &mylib_vmo); |
| ASSERT_EQ(ZX_OK, status); |
| zx_koid_t mylib_koid = GetKoid(mylib_vmo); |
| |
| // Create backing loader. |
| TestLoader backing_loader{std::move(mylib_vmo)}; |
| async::Loop backing_loop(&kAsyncLoopConfigNeverAttachToThread); |
| fidl::BindServer(backing_loop.dispatcher(), std::move(endpoints->server), &backing_loader); |
| ASSERT_EQ(ZX_OK, backing_loop.StartThread("loader-loop")); |
| |
| // Create VMO of compat driver for compat loader. |
| zx::vmo loader_vmo; |
| status = zx::vmo::create(zx_system_get_page_size(), 0, &loader_vmo); |
| ASSERT_EQ(ZX_OK, status); |
| zx_koid_t loader_koid = GetKoid(loader_vmo); |
| |
| // Create compat loader. |
| fidl::ClientEnd loader_client = std::move(endpoints->client); |
| compat::Loader loader(dispatcher(), loader_client.borrow(), std::move(loader_vmo)); |
| |
| // Create loader client. |
| endpoints = fidl::CreateEndpoints<fldsvc::Loader>(); |
| fidl::BindServer(dispatcher(), std::move(endpoints->server), &loader); |
| fidl::WireClient<fldsvc::Loader> client(std::move(endpoints->client), dispatcher()); |
| |
| // Test that loading a random library fetches a VMO from the backing loader. |
| client->LoadObject("mylib.so").Then([mylib_koid](auto& result) { |
| ASSERT_EQ(ZX_OK, result.status()); |
| auto* response = result.Unwrap(); |
| EXPECT_EQ(ZX_OK, response->rv); |
| zx_koid_t actual_koid = GetKoid(response->object); |
| EXPECT_EQ(mylib_koid, actual_koid); |
| }); |
| |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| |
| // Test that loading the driver library fetches a VMO from the compat loader. |
| client->LoadObject(compat::kLibDriverName).Then([loader_koid](auto& result) { |
| ASSERT_EQ(ZX_OK, result.status()); |
| auto* response = result.Unwrap(); |
| EXPECT_EQ(ZX_OK, response->rv); |
| zx_koid_t actual_koid = GetKoid(response->object); |
| EXPECT_EQ(loader_koid, actual_koid); |
| }); |
| |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| |
| // Test that loading the driver library a second returns an error. We should |
| // only see a single request for the driver library by the dynamic loader. |
| client->LoadObject(compat::kLibDriverName).Then([](auto& result) { |
| ASSERT_EQ(ZX_OK, result.status()); |
| auto* response = result.Unwrap(); |
| EXPECT_EQ(ZX_ERR_NOT_FOUND, response->rv); |
| }); |
| |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| } |
| |
| TEST_F(LoaderTest, DoneClosesConnection) { |
| auto endpoints = fidl::CreateEndpoints<fldsvc::Loader>(); |
| |
| // Create backing loader. |
| TestLoader backing_loader; |
| async::Loop backing_loop(&kAsyncLoopConfigNeverAttachToThread); |
| fidl::BindServer(backing_loop.dispatcher(), std::move(endpoints->server), &backing_loader); |
| ASSERT_EQ(ZX_OK, backing_loop.StartThread("loader-loop")); |
| |
| // Create compat loader. |
| compat::Loader loader(dispatcher(), endpoints->client.borrow(), zx::vmo()); |
| |
| // Create event handler. |
| TestEventHandler handler; |
| |
| // Create loader client. |
| endpoints = fidl::CreateEndpoints<fldsvc::Loader>(); |
| fidl::BindServer(dispatcher(), std::move(endpoints->server), &loader); |
| fidl::WireClient<fldsvc::Loader> client(std::move(endpoints->client), dispatcher(), &handler); |
| |
| // Test that done closes the connection. |
| [[maybe_unused]] auto result = client->Done(); |
| |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| |
| EXPECT_EQ(fidl::Reason::kPeerClosedWhileReading, handler.Reason()); |
| } |
| |
| TEST_F(LoaderTest, ConfigSucceeds) { |
| auto endpoints = fidl::CreateEndpoints<fldsvc::Loader>(); |
| |
| // Create backing loader. |
| TestLoader backing_loader; |
| async::Loop backing_loop(&kAsyncLoopConfigNeverAttachToThread); |
| fidl::BindServer(backing_loop.dispatcher(), std::move(endpoints->server), &backing_loader); |
| ASSERT_EQ(ZX_OK, backing_loop.StartThread("loader-loop")); |
| |
| // Create compat loader. |
| fidl::ClientEnd loader_client = std::move(endpoints->client); |
| compat::Loader loader(dispatcher(), loader_client.borrow(), zx::vmo()); |
| |
| // Create loader client. |
| endpoints = fidl::CreateEndpoints<fldsvc::Loader>(); |
| fidl::BindServer(dispatcher(), std::move(endpoints->server), &loader); |
| fidl::WireClient<fldsvc::Loader> client(std::move(endpoints->client), dispatcher()); |
| |
| // Test that config returns success. |
| client->Config("").Then([](auto& result) { |
| ASSERT_EQ(ZX_OK, result.status()); |
| auto* response = result.Unwrap(); |
| EXPECT_EQ(ZX_OK, response->rv); |
| }); |
| |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| } |
| |
| TEST_F(LoaderTest, CloneSucceeds) { |
| auto endpoints = fidl::CreateEndpoints<fldsvc::Loader>(); |
| |
| // Create backing loader. |
| TestLoader backing_loader; |
| async::Loop backing_loop(&kAsyncLoopConfigNeverAttachToThread); |
| fidl::BindServer(backing_loop.dispatcher(), std::move(endpoints->server), &backing_loader); |
| ASSERT_EQ(ZX_OK, backing_loop.StartThread("loader-loop")); |
| |
| // Create compat loader. |
| compat::Loader loader(dispatcher(), endpoints->client.borrow(), zx::vmo()); |
| |
| // Create loader client. |
| endpoints = fidl::CreateEndpoints<fldsvc::Loader>(); |
| fidl::BindServer(dispatcher(), std::move(endpoints->server), &loader); |
| fidl::WireClient<fldsvc::Loader> client(std::move(endpoints->client), dispatcher()); |
| |
| // Test that clone returns success. |
| endpoints = fidl::CreateEndpoints<fldsvc::Loader>(); |
| client->Clone(std::move(endpoints->server)).Then([](auto& result) { |
| ASSERT_EQ(ZX_OK, result.status()); |
| auto* response = result.Unwrap(); |
| EXPECT_EQ(ZX_OK, response->rv); |
| }); |
| |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| } |
| |
| TEST_F(LoaderTest, NoBackingLoader) { |
| auto endpoints = fidl::CreateEndpoints<fldsvc::Loader>(); |
| |
| // Create compat loader. |
| compat::Loader loader(dispatcher(), endpoints->client.borrow(), zx::vmo()); |
| |
| // Close the server end of the backing loader channel. |
| endpoints->server.reset(); |
| |
| // Create loader client. |
| endpoints = fidl::CreateEndpoints<fldsvc::Loader>(); |
| fidl::BindServer(dispatcher(), std::move(endpoints->server), &loader); |
| fidl::WireClient<fldsvc::Loader> client(std::move(endpoints->client), dispatcher()); |
| |
| // Test that functions that call the backing loader fail. |
| client->LoadObject("mylib.so").Then([](auto& result) { |
| ASSERT_EQ(ZX_OK, result.status()); |
| auto* response = result.Unwrap(); |
| EXPECT_EQ(ZX_ERR_BAD_HANDLE, response->rv); |
| }); |
| client->Config("").Then([](auto& result) { |
| ASSERT_EQ(ZX_OK, result.status()); |
| auto* response = result.Unwrap(); |
| EXPECT_EQ(ZX_ERR_BAD_HANDLE, response->rv); |
| }); |
| |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| } |