blob: f1d2da86c1e40344dbf46fa45722355be4b872f8 [file] [log] [blame]
// Copyright 2020 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/bin/driver_host/driver_host.h"
#include <fidl/fuchsia.device.manager/cpp/wire.h>
#include <fidl/fuchsia.io/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <lib/fdf/cpp/env.h>
#include <lib/fit/defer.h>
#include <lib/fit/function.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/dlfcn.h>
// The driver runtime libraries use the fdf namespace, but we would also like to use fdf
// as an alias for the fdf FIDL library.
namespace fdf {
using namespace fuchsia_driver_framework;
} // namespace fdf
namespace fdh = fuchsia_driver_host;
namespace driver_host {
DriverHost::DriverHost(inspect::Inspector& inspector, async::Loop& loop) : loop_(loop) {
inspector.GetRoot().CreateLazyNode("drivers", [this] { return Inspect(); }, &inspector);
}
fpromise::promise<inspect::Inspector> DriverHost::Inspect() {
inspect::Inspector inspector;
auto& root = inspector.GetRoot();
size_t i = 0;
std::lock_guard<std::mutex> lock(mutex_);
for (auto& driver : drivers_) {
auto child = root.CreateChild("driver-" + std::to_string(++i));
child.CreateString("url", driver.url(), &inspector);
inspector.emplace(std::move(child));
}
return fpromise::make_ok_promise(std::move(inspector));
}
zx::result<> DriverHost::PublishDriverHost(component::OutgoingDirectory& outgoing_directory) {
const auto service = [this](fidl::ServerEnd<fdh::DriverHost> request) {
fidl::BindServer(loop_.dispatcher(), std::move(request), this);
};
auto status = outgoing_directory.AddUnmanagedProtocol<fdh::DriverHost>(std::move(service));
if (status.is_error()) {
FX_SLOG(ERROR, "Failed to add directory entry",
FX_KV("name", fidl::DiscoverableProtocolName<fdh::DriverHost>),
FX_KV("status_str", status.status_string()));
}
return status;
}
void DriverHost::Start(StartRequest& request, StartCompleter::Sync& completer) {
auto callback = [this, request = std::move(request.driver()),
completer = completer.ToAsync()](zx::result<LoadedDriver> loaded) mutable {
if (loaded.is_error()) {
completer.Reply(loaded.take_error());
return;
}
async_dispatcher_t* driver_async_dispatcher = loaded->dispatcher.async_dispatcher();
// Task to start the driver. Post this to the driver dispatcher thread.
auto start_task = [this, request = std::move(request), completer = std::move(completer),
loaded = std::move(*loaded)]() mutable {
StartDriver(std::move(loaded.driver), std::move(loaded.start_args),
std::move(loaded.dispatcher), std::move(request),
[completer = std::move(completer)](zx::result<> status) mutable {
completer.Reply(status);
});
};
async::PostTask(driver_async_dispatcher, std::move(start_task));
};
LoadDriver(std::move(request.start_args()), loop_.dispatcher(), std::move(callback));
}
void DriverHost::GetProcessInfo(GetProcessInfoCompleter::Sync& completer) {
zx_info_handle_basic_t info;
zx_status_t status =
zx::process::self()->get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK) {
FX_SLOG(ERROR, "Failed to get info about process handle",
FX_KV("status_str", zx_status_get_string(status)));
completer.Reply(zx::error(status));
return;
}
uint64_t process_koid = info.koid;
status =
zx::job::default_job()->get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK) {
FX_SLOG(ERROR, "Failed to get info about job handle",
FX_KV("status_str", zx_status_get_string(status)));
completer.Reply(zx::error(status));
return;
}
uint64_t job_koid = info.koid;
completer.Reply(zx::ok(fuchsia_driver_host::ProcessInfo{{
.job_koid = job_koid,
.process_koid = process_koid,
}}));
}
void DriverHost::InstallLoader(InstallLoaderRequest& request,
InstallLoaderCompleter::Sync& completer) {
zx::handle old_handle(dl_set_loader_service(request.loader().TakeChannel().release()));
}
void DriverHost::StartDriver(fbl::RefPtr<Driver> driver,
fuchsia_driver_framework::DriverStartArgs start_args,
fdf::Dispatcher dispatcher,
fidl::ServerEnd<fuchsia_driver_host::Driver> request,
fit::callback<void(zx::result<>)> cb) {
// We have to add the driver to this list before calling Start in order to have an accurate
// count of how many drivers exist in this driver host.
{
std::lock_guard<std::mutex> lock(mutex_);
drivers_.push_back(driver);
}
auto start_callback = [this, driver, cb = std::move(cb),
request = std::move(request)](zx::result<> status) mutable {
if (status.is_error()) {
FX_SLOG(ERROR, "Failed to start driver", FX_KV("url", driver->url().data()),
FX_KV("status_str", status.status_string()));
// If we fail to start the driver, we need to initiate shutting down the driver and
// dispatchers.
ShutdownDriver(driver.get(), {});
cb(status);
return;
}
FX_SLOG(INFO, "Started driver", FX_KV("url", driver->url().data()));
auto unbind_callback = [this](Driver* driver, fidl::UnbindInfo info,
fidl::ServerEnd<fdh::Driver> server) {
if (!info.is_user_initiated()) {
FX_SLOG(WARNING, "Unexpected stop of driver", FX_KV("url", driver->url().data()),
FX_KV("status_str", info.FormatDescription()).data());
}
ShutdownDriver(driver, std::move(server));
};
auto binding = fidl::BindServer(loop_.dispatcher(), std::move(request), driver.get(),
std::move(unbind_callback));
driver->set_binding(std::move(binding));
cb(zx::ok());
};
driver->Start(driver, std::move(start_args), std::move(dispatcher), std::move(start_callback));
}
void DriverHost::ShutdownDriver(Driver* driver, fidl::ServerEnd<fdh::Driver> server) {
// This will begin shutdown of the driver's client.
driver->ShutdownClient();
// Request the driver runtime shutdown all dispatchers owned by the driver.
// Once we get the callback, we will stop the driver.
auto driver_shutdown = std::make_unique<fdf_env::DriverShutdown>();
auto driver_shutdown_ptr = driver_shutdown.get();
auto shutdown_callback = [this, driver_shutdown = std::move(driver_shutdown), driver,
server = std::move(server)](const void* shutdown_driver) mutable {
ZX_ASSERT(driver == shutdown_driver);
std::lock_guard<std::mutex> lock(mutex_);
// This removes the driver's unique_ptr from the list, which will
// run the destructor and call the driver's Destroy hook.
drivers_.erase(*driver);
// The server will not be valid when the shutdown is happening because of a start failure.
if (server.is_valid()) {
// Send the epitaph to the driver runner letting it know we stopped
// the driver correctly.
server.Close(ZX_OK);
// If this is the last driver, shutdown the driver host.
if (drivers_.is_empty()) {
// We only exit if we're not shutting down in order to match DFv1 behavior.
// TODO(https://fxbug.dev/42075187): We should always exit driver hosts when we get down to
// 0 drivers.
zx::result client = component::Connect<fuchsia_device_manager::SystemStateTransition>();
ZX_ASSERT_MSG(!client.is_error(), "Failed to connect to SystemStateTransition: %s",
client.status_string());
fidl::WireResult result = fidl::WireCall(client.value())->GetTerminationSystemState();
if (result.ok() == false ||
result->state == fuchsia_device_manager::SystemPowerState::kFullyOn) {
loop_.Quit();
}
}
}
};
// We always expect this call to succeed, as we should be the only entity
// that attempts to forcibly shutdown drivers.
ZX_ASSERT(ZX_OK == driver_shutdown_ptr->Begin(driver, std::move(shutdown_callback)));
}
} // namespace driver_host