blob: 926b4fe93d6fc027478464eec81fd7a47a82784a [file] [log] [blame]
// Copyright 2018 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 <lib/devmgr-integration-test/fixture.h>
#include <stdint.h>
#include <thread>
#include <utility>
#include <fbl/algorithm.h>
#include <fs/pseudo-dir.h>
#include <fs/service.h>
#include <fs/synchronous-vfs.h>
#include <fuchsia/boot/c/fidl.h>
#include <lib/async/cpp/wait.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/devmgr-launcher/launch.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fidl-async/bind.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
namespace {
using GetBootItemFunction = devmgr_launcher::GetBootItemFunction;
zx_status_t ItemsGet(void* ctx, uint32_t type, uint32_t extra, fidl_txn_t* txn) {
const auto& get_boot_item = *static_cast<GetBootItemFunction*>(ctx);
zx::vmo vmo;
uint32_t length = 0;
if (get_boot_item) {
zx_status_t status = get_boot_item(type, extra, &vmo, &length);
if (status != ZX_OK) {
return status;
}
}
return fuchsia_boot_ItemsGet_reply(txn, vmo.release(), length);
}
constexpr fuchsia_boot_Items_ops kItemsOps = {
.Get = ItemsGet,
};
zx_status_t RootJobGet(void* ctx, fidl_txn_t* txn) {
const auto& root_job = *static_cast<zx::unowned_job*>(ctx);
zx::job job;
zx_status_t status = root_job->duplicate(ZX_RIGHT_SAME_RIGHTS, &job);
if (status != ZX_OK) {
return status;
}
return fuchsia_boot_RootJobGet_reply(txn, job.release());
}
constexpr fuchsia_boot_RootJob_ops kRootJobOps = {
.Get = RootJobGet,
};
fbl::RefPtr<fs::Service> MakeNode(async_dispatcher_t* dispatcher, fidl_dispatch_t* dispatch,
void* ctx, const void* ops) {
return fbl::MakeRefCounted<fs::Service>([dispatcher, dispatch, ctx, ops](zx::channel channel) {
return fidl_bind(dispatcher, channel.release(), dispatch, ctx, ops);
});
}
zx_status_t bootsvc_main(zx::channel bootsvc_server, GetBootItemFunction get_boot_item,
zx::unowned_job root_job) {
async::Loop loop{&kAsyncLoopConfigNoAttachToThread};
// Quit the loop when the channel is closed.
async::Wait wait(bootsvc_server.get(), ZX_CHANNEL_PEER_CLOSED, [&loop](...) {
loop.Quit();
});
// Setup VFS.
fs::SynchronousVfs vfs(loop.dispatcher());
auto root = fbl::MakeRefCounted<fs::PseudoDir>();
auto items_dispatch = reinterpret_cast<fidl_dispatch_t*>(fuchsia_boot_Items_dispatch);
auto items_node = MakeNode(loop.dispatcher(), items_dispatch, &get_boot_item, &kItemsOps);
root->AddEntry(fuchsia_boot_Items_Name, items_node);
auto root_job_dispatch = reinterpret_cast<fidl_dispatch_t*>(fuchsia_boot_RootJob_dispatch);
auto root_job_node = MakeNode(loop.dispatcher(), root_job_dispatch, &root_job, &kRootJobOps);
root->AddEntry(fuchsia_boot_RootJob_Name, root_job_node);
// Serve VFS on channel.
auto conn = std::make_unique<fs::Connection>(&vfs, root, std::move(bootsvc_server),
ZX_FS_FLAG_DIRECTORY |
ZX_FS_RIGHT_READABLE |
ZX_FS_RIGHT_WRITABLE);
vfs.ServeConnection(std::move(conn));
return loop.Run();
}
} // namespace
namespace devmgr_integration_test {
devmgr_launcher::Args IsolatedDevmgr::DefaultArgs() {
devmgr_launcher::Args args;
args.sys_device_driver = kSysdevDriver;
args.load_drivers.push_back("/boot/driver/test.so");
args.driver_search_paths.push_back("/boot/driver/test");
args.use_system_svchost = true;
return args;
}
IsolatedDevmgr::~IsolatedDevmgr() {
// Destroy the isolated devmgr
if (job_.is_valid()) {
job_.kill();
}
}
zx_status_t IsolatedDevmgr::Create(devmgr_launcher::Args args, IsolatedDevmgr* out) {
zx::channel bootsvc_client, bootsvc_server;
zx_status_t status = zx::channel::create(0, &bootsvc_client, &bootsvc_server);
if (status != ZX_OK) {
return status;
}
GetBootItemFunction get_boot_item = std::move(args.get_boot_item);
IsolatedDevmgr devmgr;
zx::channel devfs;
status = devmgr_launcher::Launch(std::move(args), std::move(bootsvc_client), &devmgr.job_,
&devfs);
if (status != ZX_OK) {
return status;
}
// Launch bootsvc_main thread after calling devmgr_launcher::Launch, to
// avoid a race when accessing devmgr.job_.
std::thread(bootsvc_main, std::move(bootsvc_server), std::move(get_boot_item),
zx::unowned_job(devmgr.job_)).detach();
int fd;
status = fdio_fd_create(devfs.release(), &fd);
if (status != ZX_OK) {
return status;
}
devmgr.devfs_root_.reset(fd);
*out = std::move(devmgr);
return ZX_OK;
}
} // namespace devmgr_integration_test