| // 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. |
| |
| // To get drivermanager to run in a test environment, we need to fake boot-arguments & root-job. |
| |
| #include <fidl/fuchsia.boot/cpp/wire.h> |
| #include <fidl/fuchsia.component.resolution/cpp/wire.h> |
| #include <fidl/fuchsia.device.manager/cpp/wire.h> |
| #include <fidl/fuchsia.diagnostics/cpp/wire.h> |
| #include <fidl/fuchsia.driver.framework/cpp/wire.h> |
| #include <fidl/fuchsia.driver.index/cpp/wire.h> |
| #include <fidl/fuchsia.driver.test/cpp/wire.h> |
| #include <fidl/fuchsia.io/cpp/wire.h> |
| #include <fidl/fuchsia.kernel/cpp/wire.h> |
| #include <fidl/fuchsia.pkg/cpp/wire.h> |
| #include <fidl/fuchsia.power.manager/cpp/wire.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/async/cpp/wait.h> |
| #include <lib/async/dispatcher.h> |
| #include <lib/ddk/platform-defs.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fidl-async/bind.h> |
| #include <lib/fidl-async/cpp/bind.h> |
| #include <lib/stdcompat/string_view.h> |
| #include <lib/svc/dir.h> |
| #include <lib/svc/outgoing.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <lib/syslog/global.h> |
| #include <lib/vfs/cpp/remote_dir.h> |
| #include <lib/zx/job.h> |
| #include <lib/zx/time.h> |
| #include <lib/zx/vmo.h> |
| #include <zircon/boot/image.h> |
| #include <zircon/status.h> |
| |
| #include <memory> |
| #include <vector> |
| |
| #include <ddk/metadata/test.h> |
| #include <fbl/string_printf.h> |
| #include <mock-boot-arguments/server.h> |
| |
| #include "lib/vfs/cpp/pseudo_dir.h" |
| #include "src/lib/fxl/strings/join_strings.h" |
| #include "src/lib/storage/vfs/cpp/pseudo_dir.h" |
| #include "src/lib/storage/vfs/cpp/pseudo_file.h" |
| #include "src/lib/storage/vfs/cpp/remote_dir.h" |
| |
| namespace { |
| |
| constexpr zx_signals_t kDriverTestRealmStartSignal = ZX_USER_SIGNAL_1; |
| |
| const char* LogLevelToString(fuchsia_diagnostics::wire::Severity severity) { |
| switch (severity) { |
| case fuchsia_diagnostics::wire::Severity::kTrace: |
| return "TRACE"; |
| case fuchsia_diagnostics::wire::Severity::kDebug: |
| return "DEBUG"; |
| case fuchsia_diagnostics::wire::Severity::kInfo: |
| return "INFO"; |
| case fuchsia_diagnostics::wire::Severity::kWarn: |
| return "WARN"; |
| case fuchsia_diagnostics::wire::Severity::kError: |
| return "ERROR"; |
| case fuchsia_diagnostics::wire::Severity::kFatal: |
| return "FATAL"; |
| } |
| } |
| |
| // This board driver knows how to interpret the metadata for which devices to |
| // spawn. |
| const zbi_platform_id_t kPlatformId = []() { |
| zbi_platform_id_t plat_id = {}; |
| plat_id.vid = PDEV_VID_TEST; |
| plat_id.pid = PDEV_PID_PBUS_TEST; |
| strcpy(plat_id.board_name, "driver-integration-test"); |
| return plat_id; |
| }(); |
| |
| #define BOARD_REVISION_TEST 42 |
| |
| const zbi_board_info_t kBoardInfo = []() { |
| zbi_board_info_t board_info = {}; |
| board_info.revision = BOARD_REVISION_TEST; |
| return board_info; |
| }(); |
| |
| // This function is responsible for serializing driver data. It must be kept |
| // updated with the function that deserialized the data. This function |
| // is TestBoard::FetchAndDeserialize. |
| zx_status_t GetBootItem(const std::vector<board_test::DeviceEntry>& entries, uint32_t type, |
| std::string_view board_name, uint32_t extra, zx::vmo* out, |
| uint32_t* length) { |
| zx::vmo vmo; |
| switch (type) { |
| case ZBI_TYPE_PLATFORM_ID: { |
| zbi_platform_id_t platform_id = kPlatformId; |
| if (!board_name.empty()) { |
| strncpy(platform_id.board_name, board_name.data(), ZBI_BOARD_NAME_LEN - 1); |
| } |
| zx_status_t status = zx::vmo::create(sizeof(kPlatformId), 0, &vmo); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = vmo.write(&platform_id, 0, sizeof(kPlatformId)); |
| if (status != ZX_OK) { |
| return status; |
| } |
| *length = sizeof(kPlatformId); |
| break; |
| } |
| case ZBI_TYPE_DRV_BOARD_INFO: { |
| zx_status_t status = zx::vmo::create(sizeof(kBoardInfo), 0, &vmo); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = vmo.write(&kBoardInfo, 0, sizeof(kBoardInfo)); |
| if (status != ZX_OK) { |
| return status; |
| } |
| *length = sizeof(kBoardInfo); |
| break; |
| } |
| case ZBI_TYPE_DRV_BOARD_PRIVATE: { |
| size_t list_size = sizeof(board_test::DeviceList); |
| size_t entry_size = entries.size() * sizeof(board_test::DeviceEntry); |
| |
| size_t metadata_size = 0; |
| for (const board_test::DeviceEntry& entry : entries) { |
| metadata_size += entry.metadata_size; |
| } |
| |
| zx_status_t status = zx::vmo::create(list_size + entry_size + metadata_size, 0, &vmo); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Write DeviceList to vmo. |
| board_test::DeviceList list{.count = entries.size()}; |
| status = vmo.write(&list, 0, sizeof(list)); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Write DeviceEntries to vmo. |
| status = vmo.write(entries.data(), list_size, entry_size); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Write Metadata to vmo. |
| size_t write_offset = list_size + entry_size; |
| for (const board_test::DeviceEntry& entry : entries) { |
| status = vmo.write(entry.metadata, write_offset, entry.metadata_size); |
| if (status != ZX_OK) { |
| return status; |
| } |
| write_offset += entry.metadata_size; |
| } |
| |
| *length = static_cast<uint32_t>(list_size + entry_size + metadata_size); |
| break; |
| } |
| default: |
| break; |
| } |
| *out = std::move(vmo); |
| return ZX_OK; |
| } |
| |
| class FakePowerRegistration |
| : public fidl::WireServer<fuchsia_power_manager::DriverManagerRegistration> { |
| public: |
| void Register(RegisterRequestView request, RegisterCompleter::Sync& completer) override { |
| // Store these so the other side doesn't see the channels close. |
| transition_ = std::move(request->system_state_transition); |
| dir_ = std::move(request->dir); |
| completer.ReplySuccess(); |
| } |
| |
| private: |
| fidl::ClientEnd<fuchsia_device_manager::SystemStateTransition> transition_; |
| fidl::ClientEnd<fuchsia_io::Directory> dir_; |
| }; |
| |
| class FakeBootItems final : public fidl::WireServer<fuchsia_boot::Items> { |
| public: |
| void Get(GetRequestView request, GetCompleter::Sync& completer) override { |
| zx::vmo vmo; |
| uint32_t length = 0; |
| std::vector<board_test::DeviceEntry> entries = {}; |
| zx_status_t status = |
| GetBootItem(entries, request->type, board_name_, request->extra, &vmo, &length); |
| if (status != ZX_OK) { |
| FX_LOGF(ERROR, nullptr, "Failed to get boot items: %d", status); |
| } |
| completer.Reply(std::move(vmo), length); |
| } |
| |
| void GetBootloaderFile(GetBootloaderFileRequestView request, |
| GetBootloaderFileCompleter::Sync& completer) override { |
| completer.Reply(zx::vmo()); |
| } |
| |
| std::string board_name_; |
| }; |
| |
| class FakeDriverIndex final : public fidl::WireServer<fuchsia_driver_index::DriverIndex> { |
| void MatchDriver(MatchDriverRequestView request, MatchDriverCompleter::Sync& completer) override { |
| completer.ReplyError(ZX_ERR_NOT_FOUND); |
| } |
| |
| void WaitForBaseDrivers(WaitForBaseDriversRequestView request, |
| WaitForBaseDriversCompleter::Sync& completer) override { |
| completer.Reply(); |
| } |
| void MatchDriversV1(MatchDriversV1RequestView request, |
| MatchDriversV1Completer::Sync& completer) override { |
| completer.ReplyError(ZX_ERR_NOT_FOUND); |
| } |
| void AddDeviceGroup(AddDeviceGroupRequestView request, |
| AddDeviceGroupCompleter::Sync& completer) override { |
| completer.ReplyError(ZX_ERR_NOT_FOUND); |
| } |
| }; |
| |
| class FakeRootJob final : public fidl::WireServer<fuchsia_kernel::RootJob> { |
| void Get(GetRequestView request, GetCompleter::Sync& completer) override { |
| zx::job job; |
| zx_status_t status = zx::job::default_job()->duplicate(ZX_RIGHT_SAME_RIGHTS, &job); |
| if (status != ZX_OK) { |
| FX_LOGF(ERROR, nullptr, "Failed to duplicate job: %d", status); |
| } |
| completer.Reply(std::move(job)); |
| } |
| }; |
| |
| class FakeBootResolver final : public fidl::WireServer<fuchsia_component_resolution::Resolver> { |
| public: |
| void SetPkgDir(fbl::RefPtr<fs::RemoteDir> pkg_dir) { pkg_dir_ = std::move(pkg_dir); } |
| |
| private: |
| void Resolve(ResolveRequestView request, ResolveCompleter::Sync& completer) override { |
| std::string_view kPrefix = "fuchsia-boot:///"; |
| std::string_view relative_path = request->component_url.get(); |
| if (!cpp20::starts_with(relative_path, kPrefix)) { |
| completer.ReplyError(fuchsia_component_resolution::wire::ResolverError::kInvalidArgs); |
| return; |
| } |
| relative_path.remove_prefix(kPrefix.size() + 1); |
| |
| auto file = fidl::CreateEndpoints<fuchsia_io::File>(); |
| if (file.is_error()) { |
| completer.ReplyError(fuchsia_component_resolution::wire::ResolverError::kInternal); |
| return; |
| } |
| zx_status_t status = |
| fdio_open_at(pkg_dir_->GetRemote().channel()->get(), std::string(relative_path).data(), |
| static_cast<uint32_t>(fuchsia_io::wire::OpenFlags::kRightReadable), |
| file->server.channel().release()); |
| if (status != ZX_OK) { |
| completer.ReplyError(fuchsia_component_resolution::wire::ResolverError::kInternal); |
| return; |
| } |
| fidl::WireResult result = |
| fidl::WireCall(file->client)->GetBackingMemory(fuchsia_io::wire::VmoFlags::kRead); |
| if (!result.ok()) { |
| completer.ReplyError(fuchsia_component_resolution::wire::ResolverError::kIo); |
| return; |
| } |
| auto& response = result.value(); |
| if (response.is_error()) { |
| completer.ReplyError(fuchsia_component_resolution::wire::ResolverError::kIo); |
| return; |
| } |
| zx::vmo& vmo = response.value()->vmo; |
| uint64_t size; |
| status = vmo.get_prop_content_size(&size); |
| if (status != ZX_OK) { |
| completer.ReplyError(fuchsia_component_resolution::wire::ResolverError::kIo); |
| } |
| |
| fidl::ClientEnd<fuchsia_io::Directory> directory( |
| zx::channel(fdio_service_clone(pkg_dir_->GetRemote().channel()->get()))); |
| if (!directory.is_valid()) { |
| completer.ReplyError(fuchsia_component_resolution::wire::ResolverError::kInternal); |
| return; |
| } |
| |
| fidl::Arena arena; |
| fuchsia_component_resolution::wire::Package package(arena); |
| package.set_url(arena, fidl::StringView::FromExternal(kPrefix)); |
| package.set_directory(std::move(directory)); |
| |
| fuchsia_component_resolution::wire::Component component(arena); |
| component.set_url(arena, request->component_url); |
| component.set_decl(arena, fuchsia_mem::wire::Data::WithBuffer(arena, fuchsia_mem::wire::Buffer{ |
| .vmo = std::move(vmo), |
| .size = size, |
| })); |
| component.set_package(arena, std::move(package)); |
| completer.ReplySuccess(std::move(component)); |
| } |
| |
| void ResolveWithContext(ResolveWithContextRequestView request, |
| ResolveWithContextCompleter::Sync& completer) override { |
| FX_LOGF(ERROR, nullptr, "FakeBootResolver does not currently support ResolveWithContext"); |
| completer.ReplyError(fuchsia_component_resolution::wire::ResolverError::kInvalidArgs); |
| } |
| |
| fbl::RefPtr<fs::RemoteDir> pkg_dir_; |
| }; |
| |
| class FakePackageResolver final : public fidl::WireServer<fuchsia_pkg::PackageResolver> { |
| void Resolve(ResolveRequestView request, ResolveCompleter::Sync& completer) override { |
| auto status = fdio_open("/pkg", |
| static_cast<uint32_t>(fuchsia_io::wire::OpenFlags::kDirectory | |
| fuchsia_io::wire::OpenFlags::kRightReadable | |
| fuchsia_io::wire::OpenFlags::kRightExecutable), |
| request->dir.TakeChannel().release()); |
| if (status != ZX_OK) { |
| completer.ReplyError(fuchsia_pkg::wire::ResolveError::kInternal); |
| return; |
| } |
| |
| completer.ReplySuccess(); |
| } |
| |
| void GetHash(GetHashRequestView request, GetHashCompleter::Sync& completer) override { |
| completer.ReplyError(ZX_ERR_PROTOCOL_NOT_SUPPORTED); |
| } |
| }; |
| |
| class DriverTestRealm final : public fidl::WireServer<fuchsia_driver_test::Realm> { |
| public: |
| DriverTestRealm(svc::Outgoing* outgoing, async::Loop* loop) : outgoing_(outgoing), loop_(loop) {} |
| |
| static zx::status<std::unique_ptr<DriverTestRealm>> Create(svc::Outgoing* outgoing, |
| async::Loop* loop) { |
| auto realm = std::make_unique<DriverTestRealm>(outgoing, loop); |
| zx_status_t status = realm->Initialize(); |
| if (status != ZX_OK) { |
| return zx::error(status); |
| } |
| return zx::ok(std::move(realm)); |
| } |
| |
| void Start(StartRequestView request, StartCompleter::Sync& completer) override { |
| if (is_started_) { |
| completer.ReplyError(ZX_ERR_ALREADY_EXISTS); |
| return; |
| } |
| |
| if (request->args.has_board_name()) { |
| boot_items_.board_name_ = |
| std::string(request->args.board_name().data(), request->args.board_name().size()); |
| } |
| |
| auto boot_args = CreateBootArgs(request); |
| for (std::pair<std::string, std::string> boot_arg : boot_args) { |
| if (boot_arg.first.size() > fuchsia_boot::wire::kMaxArgsNameLength) { |
| FX_LOGF(ERROR, nullptr, |
| "The length of the name of the boot argument \"%.*s\" is too long: The length of " |
| "the boot argument's name must be less than or equal to %d", |
| (int)boot_arg.first.size(), boot_arg.first.data(), |
| fuchsia_boot::wire::kMaxArgsNameLength); |
| completer.ReplyError(ZX_ERR_INVALID_ARGS); |
| return; |
| } |
| |
| if (boot_arg.second.size() > fuchsia_boot::wire::kMaxArgsValueLength) { |
| FX_LOGF(ERROR, nullptr, |
| "The length of the value of the boot argument \"%.*s\", which is \"%*.s\", is too " |
| "long: The length of the boot argument's value must be less than or equal to %d", |
| (int)boot_arg.first.size(), boot_arg.first.data(), (int)boot_arg.second.size(), |
| boot_arg.second.data(), fuchsia_boot::wire::kMaxArgsValueLength); |
| completer.ReplyError(ZX_ERR_INVALID_ARGS); |
| return; |
| } |
| } |
| boot_arguments_ = mock_boot_arguments::Server(std::move(boot_args)); |
| |
| fidl::ClientEnd<fuchsia_io::Directory> boot_dir; |
| if (request->args.has_boot()) { |
| boot_dir = fidl::ClientEnd<fuchsia_io::Directory>(std::move(request->args.boot())); |
| } else { |
| auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>(); |
| if (endpoints.is_error()) { |
| completer.ReplyError(ZX_ERR_INTERNAL); |
| return; |
| } |
| zx_status_t status = |
| fdio_open("/pkg", |
| static_cast<uint32_t>(fuchsia_io::wire::OpenFlags::kDirectory | |
| fuchsia_io::wire::OpenFlags::kRightReadable | |
| fuchsia_io::wire::OpenFlags::kRightExecutable), |
| endpoints->server.channel().release()); |
| if (status != ZX_OK) { |
| completer.ReplyError(ZX_ERR_INTERNAL); |
| return; |
| } |
| boot_dir = std::move(endpoints->client); |
| } |
| |
| auto remote_dir = fbl::MakeRefCounted<fs::RemoteDir>(std::move(boot_dir)); |
| boot_resolver_.SetPkgDir(remote_dir); |
| outgoing_->root_dir()->AddEntry("boot", remote_dir); |
| |
| start_event_.signal(0, kDriverTestRealmStartSignal); |
| completer.ReplySuccess(); |
| } |
| |
| private: |
| zx_status_t Initialize() { |
| zx_status_t status = zx::event::create(0, &start_event_); |
| if (status != ZX_OK) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| status = AddProtocol<fuchsia_driver_test::Realm>(this); |
| if (status != ZX_OK) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| status = AddProtocolWithWait<fuchsia_boot::Arguments>(&boot_arguments_); |
| if (status != ZX_OK) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| status = AddProtocolWithWait<fuchsia_boot::Items>(&boot_items_); |
| if (status != ZX_OK) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| status = AddProtocolWithWait<fuchsia_kernel::RootJob>(&root_job_); |
| if (status != ZX_OK) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| status = AddProtocolWithWait<fuchsia_component_resolution::Resolver>(&boot_resolver_); |
| if (status != ZX_OK) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| status = AddProtocolWithWait<fuchsia_power_manager::DriverManagerRegistration>( |
| &fake_power_registration_); |
| if (status != ZX_OK) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| status = AddProtocolWithWait<fuchsia_pkg::PackageResolver>(&package_resolver_); |
| if (status != ZX_OK) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| status = InitializeDirectories(); |
| if (status != ZX_OK) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| return ZX_OK; |
| } |
| |
| std::map<std::string, std::string> CreateBootArgs(StartRequestView& request) { |
| std::map<std::string, std::string> boot_args; |
| |
| bool is_dfv2 = false; |
| if (request->args.has_use_driver_framework_v2()) { |
| is_dfv2 = request->args.use_driver_framework_v2(); |
| } |
| |
| boot_args["devmgr.enable-ephemeral"] = "true"; |
| boot_args["devmgr.require-system"] = "true"; |
| if (is_dfv2) { |
| boot_args["driver_manager.use_driver_framework_v2"] = "true"; |
| } |
| if (request->args.has_root_driver()) { |
| boot_args["driver_manager.root-driver"] = |
| std::string(request->args.root_driver().data(), request->args.root_driver().size()); |
| } else { |
| boot_args["driver_manager.root-driver"] = "fuchsia-boot:///#driver/test-parent-sys.so"; |
| } |
| |
| if (request->args.has_driver_tests_enable_all() && request->args.driver_tests_enable_all()) { |
| boot_args["driver.tests.enable"] = "true"; |
| } |
| |
| if (request->args.has_driver_tests_enable()) { |
| for (auto& driver : request->args.driver_tests_enable()) { |
| auto string = fbl::StringPrintf("driver.%s.tests.enable", driver.data()); |
| boot_args[string.data()] = "true"; |
| } |
| } |
| |
| if (request->args.has_driver_tests_disable()) { |
| for (auto& driver : request->args.driver_tests_disable()) { |
| auto string = fbl::StringPrintf("driver.%s.tests.enable", driver.data()); |
| boot_args[string.data()] = "false"; |
| } |
| } |
| |
| if (request->args.has_driver_log_level()) { |
| for (auto& driver : request->args.driver_log_level()) { |
| auto string = fbl::StringPrintf("driver.%s.log", driver.name.data()); |
| boot_args[string.data()] = LogLevelToString(driver.log_level); |
| } |
| } |
| |
| if (request->args.has_driver_disable()) { |
| std::vector<std::string_view> drivers(request->args.driver_disable().count()); |
| for (auto& driver : request->args.driver_disable()) { |
| drivers.emplace_back(std::string_view(driver.data())); |
| auto string = fbl::StringPrintf("driver.%s.disable", driver.data()); |
| boot_args[string.data()] = "true"; |
| } |
| boot_args["devmgr.disabled-drivers"] = fxl::JoinStrings(drivers, ","); |
| } |
| |
| if (request->args.has_driver_bind_eager() && request->args.driver_bind_eager().count() > 0) { |
| std::vector<std::string_view> drivers(request->args.driver_bind_eager().count()); |
| for (auto& driver : request->args.driver_bind_eager()) { |
| drivers.emplace_back(driver.data()); |
| } |
| boot_args["devmgr.bind-eager"] = fxl::JoinStrings(drivers, ","); |
| } |
| |
| return boot_args; |
| } |
| |
| zx_status_t InitializeDirectories() { |
| auto system = fbl::MakeRefCounted<fs::PseudoDir>(); |
| system->AddEntry("drivers", fbl::MakeRefCounted<fs::PseudoDir>()); |
| outgoing_->root_dir()->AddEntry("system", std::move(system)); |
| |
| auto pkgfs = fbl::MakeRefCounted<fs::PseudoDir>(); |
| // Add the necessary empty base driver manifest. |
| // It's added to /pkgfs/packages/driver-manager-base-config/0/config/base-driver-manifest.json |
| { |
| auto packages = fbl::MakeRefCounted<fs::PseudoDir>(); |
| auto driver_manager_base_config = fbl::MakeRefCounted<fs::PseudoDir>(); |
| auto zero = fbl::MakeRefCounted<fs::PseudoDir>(); |
| auto config = fbl::MakeRefCounted<fs::PseudoDir>(); |
| auto base_driver_manifest = fbl::MakeRefCounted<fs::UnbufferedPseudoFile>( |
| [](fbl::String* output) { |
| // Return an empty JSON array. |
| *output = fbl::String("[]"); |
| return ZX_OK; |
| }, |
| [](std::string_view input) { return ZX_ERR_NOT_SUPPORTED; }); |
| |
| config->AddEntry("base-driver-manifest.json", std::move(base_driver_manifest)); |
| zero->AddEntry("config", std::move(config)); |
| driver_manager_base_config->AddEntry("0", std::move(zero)); |
| packages->AddEntry("driver-manager-base-config", std::move(driver_manager_base_config)); |
| pkgfs->AddEntry("packages", std::move(packages)); |
| } |
| outgoing_->root_dir()->AddEntry("pkgfs", std::move(pkgfs)); |
| return ZX_OK; |
| } |
| |
| template <class Protocol> |
| zx_status_t AddProtocolWithWait(fidl::WireServer<Protocol>* server) { |
| auto service_callback = [this, server](fidl::ServerEnd<Protocol> request) { |
| auto wait = |
| std::make_shared<async::WaitOnce>(start_event_.get(), kDriverTestRealmStartSignal); |
| auto wait_callback = [wait_object = wait, request = std::move(request), server]( |
| async_dispatcher_t* dispatcher, async::WaitOnce* wait, |
| zx_status_t status, const zx_packet_signal_t* signal) mutable { |
| if (status == ZX_OK) { |
| fidl::BindServer(dispatcher, std::move(request), server); |
| } |
| }; |
| return wait->Begin(loop_->dispatcher(), std::move(wait_callback)); |
| }; |
| |
| return outgoing_->svc_dir()->AddEntry( |
| fidl::DiscoverableProtocolName<Protocol>, |
| fbl::MakeRefCounted<fs::Service>(std::move(service_callback))); |
| } |
| |
| template <class Protocol> |
| zx_status_t AddProtocol(fidl::WireServer<Protocol>* server) { |
| auto service_callback = [this, server](fidl::ServerEnd<Protocol> request) { |
| fidl::BindServer(loop_->dispatcher(), std::move(request), server); |
| return ZX_OK; |
| }; |
| return outgoing_->svc_dir()->AddEntry( |
| fidl::DiscoverableProtocolName<Protocol>, |
| fbl::MakeRefCounted<fs::Service>(std::move(service_callback))); |
| } |
| |
| svc::Outgoing* outgoing_; |
| async::Loop* loop_; |
| |
| mock_boot_arguments::Server boot_arguments_; |
| FakePowerRegistration fake_power_registration_; |
| FakeBootItems boot_items_; |
| FakeRootJob root_job_; |
| FakeBootResolver boot_resolver_; |
| FakePackageResolver package_resolver_; |
| |
| zx::event start_event_; |
| bool is_started_ = false; |
| }; |
| |
| } // namespace |
| |
| int main() { |
| async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); |
| |
| svc::Outgoing outgoing(loop.dispatcher()); |
| zx_status_t status = outgoing.ServeFromStartupInfo(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| auto realm = DriverTestRealm::Create(&outgoing, &loop); |
| if (realm.status_value() != ZX_OK) { |
| return realm.status_value(); |
| } |
| |
| loop.Run(); |
| return 0; |
| } |