blob: 3c8637559f45260623a8c72ffd76dcea493e6413 [file] [log] [blame]
// Copyright 2017 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_manager/coordinator.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <fidl/fuchsia.io/cpp/wire.h>
#include <fidl/fuchsia.pkg/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/cpp/receiver.h>
#include <lib/async/cpp/task.h>
#include <lib/ddk/driver.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/io.h>
#include <lib/fidl-async/bind.h>
#include <lib/fidl-async/cpp/bind.h>
#include <lib/fidl/coding.h>
#include <lib/fidl/llcpp/arena.h>
#include <lib/fidl/llcpp/wire_messaging.h>
#include <lib/fit/defer.h>
#include <lib/fzl/owned-vmo-mapper.h>
#include <lib/service/llcpp/service.h>
#include <lib/zbitl/error-string.h>
#include <lib/zbitl/image.h>
#include <lib/zbitl/item.h>
#include <lib/zbitl/vmo.h>
#include <lib/zircon-internal/ktrace.h>
#include <lib/zx/clock.h>
#include <lib/zx/job.h>
#include <lib/zx/time.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <threads.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/policy.h>
#include <zircon/syscalls/system.h>
#include <array>
#include <cstdint>
#include <memory>
#include <string_view>
#include <utility>
#include <vector>
#include <driver-info/driver-info.h>
#include <fbl/string_printf.h>
#include <inspector/inspector.h>
#include <src/bringup/lib/mexec/mexec.h>
#include <src/lib/fsl/vmo/sized_vmo.h>
#include <src/lib/fsl/vmo/vector.h>
#include "src/devices/bin/driver_manager/driver_host_loader_service.h"
#include "src/devices/bin/driver_manager/manifest_parser.h"
#include "src/devices/bin/driver_manager/package_resolver.h"
#include "src/devices/bin/driver_manager/v1/driver_development.h"
#include "src/devices/bin/driver_manager/v1/unbind_task.h"
#include "src/devices/lib/log/log.h"
#include "src/lib/storage/vfs/cpp/pseudo_dir.h"
namespace fio = fuchsia_io;
namespace {
namespace fdd = fuchsia_driver_development;
namespace fdm = fuchsia_device_manager;
namespace fdr = fuchsia_driver_registrar;
namespace fpm = fuchsia_power_manager;
constexpr char kDriverHostPath[] = "/pkg/bin/driver_host";
constexpr const char* kItemsPath = fidl::DiscoverableProtocolDefaultPath<fuchsia_boot::Items>;
// The driver_host doesn't just define its own __asan_default_options()
// function because that conflicts with the build-system feature of injecting
// such a function based on the `asan_default_options` GN build argument.
// Since driver_host is only ever launched here, it can always get its
// necessary options through its environment variables. The sanitizer
// runtime combines the __asan_default_options() and environment settings.
constexpr char kAsanEnvironment[] =
"ASAN_OPTIONS="
// All drivers have a pure C ABI. But each individual driver might
// statically link in its own copy of some C++ library code. Since no
// C++ language relationships leak through the driver ABI, each driver is
// its own whole program from the perspective of the C++ language rules.
// But the ASan runtime doesn't understand this and wants to diagnose ODR
// violations when the same global is defined in multiple drivers, which
// is likely with C++ library use. There is no real way to teach the
// ASan instrumentation or runtime about symbol visibility and isolated
// worlds within the program, so the only thing to do is suppress the ODR
// violation detection. This unfortunately means real ODR violations
// within a single C++ driver won't be caught either.
"detect_odr_violation=0";
// Currently we check if DriverManager is built using ASAN.
// If it is, then we assume DriverHost is also ASAN.
//
// We currently assume that either the whole system is ASAN or the whole
// system is non-ASAN. One day we might be able to be more flexible about
// which drivers must get loaded into the same driver_host and thus be able
// to use both ASan and non-ASan driver_hosts at the same time when only
// a subset of drivers use ASan.
bool driver_host_is_asan() {
bool is_asan = false;
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
is_asan = true;
#endif
#endif
return is_asan;
}
// send message to driver_host, requesting the creation of a device
zx_status_t CreateProxyDevice(const fbl::RefPtr<Device>& dev, fbl::RefPtr<DriverHost>& dh,
const char* args, zx::channel rpc_proxy) {
auto coordinator_endpoints = fidl::CreateEndpoints<fdm::Coordinator>();
if (coordinator_endpoints.is_error()) {
return coordinator_endpoints.error_value();
}
auto device_controller_request = dev->ConnectDeviceController(dev->coordinator->dispatcher());
fidl::Arena arena;
if (dev->libname().size() != 0) {
zx::vmo vmo;
if (auto status = dev->coordinator->LibnameToVmo(dev->libname(), &vmo); status != ZX_OK) {
return status;
}
auto driver_path = fidl::StringView::FromExternal(dev->libname().data(), dev->libname().size());
auto args_view = fidl::StringView::FromExternal(args, strlen(args));
fdm::wire::ProxyDevice proxy{driver_path, std::move(vmo), std::move(rpc_proxy), args_view};
auto type = fdm::wire::DeviceType::WithProxy(arena, std::move(proxy));
dh->controller()
->CreateDevice(std::move(coordinator_endpoints->client),
std::move(device_controller_request), std::move(type), dev->local_id())
.ThenExactlyOnce(
[](fidl::WireUnownedResult<fdm::DriverHostController::CreateDevice>& result) {
if (!result.ok()) {
LOGF(ERROR, "Failed to create device: %s",
result.error().FormatDescription().c_str());
return;
}
if (result.value().status != ZX_OK) {
LOGF(ERROR, "Failed to create device: %s",
zx_status_get_string(result.value().status));
}
});
} else {
fdm::wire::StubDevice stub{dev->protocol_id()};
auto type = fdm::wire::DeviceType::WithStub(stub);
dh->controller()
->CreateDevice(std::move(coordinator_endpoints->client),
std::move(device_controller_request), std::move(type), dev->local_id())
.ThenExactlyOnce(
[](fidl::WireUnownedResult<fdm::DriverHostController::CreateDevice>& result) {
if (!result.ok()) {
LOGF(ERROR, "Failed to create device: %s",
result.error().FormatDescription().c_str());
return;
}
if (result.value().status != ZX_OK) {
LOGF(ERROR, "Failed to create device: %s",
zx_status_get_string(result.value().status));
}
});
}
Device::Bind(dev, dev->coordinator->dispatcher(), std::move(coordinator_endpoints->server));
return ZX_OK;
}
zx_status_t CreateNewProxyDevice(const fbl::RefPtr<Device>& dev, fbl::RefPtr<DriverHost>& dh,
fidl::ClientEnd<fio::Directory> incoming_dir) {
auto coordinator_endpoints = fidl::CreateEndpoints<fdm::Coordinator>();
if (coordinator_endpoints.is_error()) {
return coordinator_endpoints.error_value();
}
auto device_controller_request = dev->ConnectDeviceController(dev->coordinator->dispatcher());
fdm::wire::NewProxyDevice new_proxy{std::move(incoming_dir)};
auto type = fdm::wire::DeviceType::WithNewProxy(std::move(new_proxy));
dh->controller()
->CreateDevice(std::move(coordinator_endpoints->client), std::move(device_controller_request),
std::move(type), dev->local_id())
.ThenExactlyOnce(
[](fidl::WireUnownedResult<fdm::DriverHostController::CreateDevice>& result) {
if (!result.ok()) {
LOGF(ERROR, "Failed to create device: %s",
result.error().FormatDescription().c_str());
return;
}
if (result.value().status != ZX_OK) {
LOGF(ERROR, "Failed to create device: %s",
zx_status_get_string(result.value().status));
}
});
Device::Bind(dev, dev->coordinator->dispatcher(), std::move(coordinator_endpoints->server));
return ZX_OK;
}
// Binds the driver to the device by sending a request to driver_host.
zx_status_t BindDriverToDevice(const fbl::RefPtr<Device>& dev, const char* libname) {
zx::vmo vmo;
zx_status_t status = dev->coordinator->LibnameToVmo(libname, &vmo);
if (status != ZX_OK) {
return status;
}
dev->flags |= DEV_CTX_BOUND;
dev->device_controller()
->BindDriver(fidl::StringView::FromExternal(libname, strlen(libname)), std::move(vmo))
.ThenExactlyOnce([dev](fidl::WireUnownedResult<fdm::DeviceController::BindDriver>& result) {
if (!result.ok()) {
LOGF(ERROR, "Failed to bind driver '%s': %s", dev->name().data(), result.status_string());
dev->flags &= (~DEV_CTX_BOUND);
return;
}
if (result.value().status != ZX_OK) {
LOGF(ERROR, "Failed to bind driver '%s': %s", dev->name().data(),
zx_status_get_string(result.value().status));
dev->flags &= (~DEV_CTX_BOUND);
return;
}
});
return ZX_OK;
}
} // namespace
namespace statecontrol_fidl = fuchsia_hardware_power_statecontrol;
Coordinator::Coordinator(CoordinatorConfig config, InspectManager* inspect_manager,
async_dispatcher_t* dispatcher, async_dispatcher_t* firmware_dispatcher)
: config_(std::move(config)),
dispatcher_(dispatcher),
base_resolver_(config_.boot_args),
inspect_manager_(inspect_manager),
package_resolver_(config_.boot_args),
driver_loader_(config_.boot_args, std::move(config_.driver_index), &base_resolver_,
dispatcher, config_.require_system, &package_resolver_) {
shutdown_system_state_ = config_.default_shutdown_system_state;
root_device_ =
fbl::MakeRefCounted<Device>(this, "root", fbl::String(), "root,", nullptr, ZX_PROTOCOL_ROOT,
zx::vmo(), zx::channel(), fidl::ClientEnd<fio::Directory>());
root_device_->flags = DEV_CTX_IMMORTAL | DEV_CTX_MUST_ISOLATE;
bind_driver_manager_ =
std::make_unique<BindDriverManager>(this, fit::bind_member<&Coordinator::AttemptBind>(this));
device_manager_ = std::make_unique<DeviceManager>(this, config_.crash_policy);
suspend_resume_manager_ = std::make_unique<SuspendResumeManager>(this, config_.suspend_timeout);
firmware_loader_ =
std::make_unique<FirmwareLoader>(this, firmware_dispatcher, config_.path_prefix);
debug_dump_ = std::make_unique<DebugDump>(this);
}
Coordinator::~Coordinator() {}
void Coordinator::LoadV1Drivers(std::string_view sys_device_driver,
fbl::Vector<std::string>& driver_search_paths,
fbl::Vector<const char*>& load_drivers) {
InitCoreDevices(sys_device_driver);
// Load the drivers.
for (const std::string& path : driver_search_paths) {
find_loadable_drivers(boot_args(), path, fit::bind_member<&Coordinator::DriverAddedInit>(this));
}
for (const char* driver : load_drivers) {
load_driver(boot_args(), driver, fit::bind_member<&Coordinator::DriverAddedInit>(this));
}
PrepareProxy(sys_device_, nullptr);
// Bind all the drivers we loaded.
AddAndBindDrivers(std::move(drivers_));
DriverLoader::MatchDeviceConfig config;
bind_driver_manager_->BindAllDevicesDriverIndex(config);
// Bind the fallback drivers if we don't require the full system.
if (config_.require_system) {
LOGF(INFO, "Full system required, fallback drivers will be loaded after '/system' is loaded");
} else {
BindFallbackDrivers();
}
// Schedule the base drivers to load.
driver_loader_.WaitForBaseDrivers([this]() {
DriverLoader::MatchDeviceConfig config;
config.only_return_base_and_fallback_drivers = true;
bind_driver_manager_->BindAllDevicesDriverIndex(config);
});
devfs_publish(root_device_, sys_device_);
// TODO(https://fxbug.dev/99076) Remove this when this issue is fixed.
LOGF(INFO, "V1 drivers loaded and published");
}
void Coordinator::InitCoreDevices(std::string_view sys_device_driver) {
// If the sys device is not a path, then we try to load it like a URL.
if (sys_device_driver[0] != '/') {
auto string = std::string(sys_device_driver.data());
driver_loader_.LoadDriverUrl(string);
}
sys_device_ =
fbl::MakeRefCounted<Device>(this, "sys", sys_device_driver, "sys,", root_device_, 0,
zx::vmo(), zx::channel(), fidl::ClientEnd<fio::Directory>());
sys_device_->flags = DEV_CTX_IMMORTAL | DEV_CTX_MUST_ISOLATE;
}
void Coordinator::RegisterWithPowerManager(fidl::ClientEnd<fio::Directory> devfs,
RegisterWithPowerManagerCompletion completion) {
auto system_state_endpoints = fidl::CreateEndpoints<fdm::SystemStateTransition>();
if (system_state_endpoints.is_error()) {
completion(system_state_endpoints.error_value());
return;
}
std::unique_ptr<SystemStateManager> system_state_manager;
auto status = SystemStateManager::Create(
dispatcher_, this, std::move(system_state_endpoints->server), &system_state_manager);
if (status != ZX_OK) {
completion(status);
return;
}
set_system_state_manager(std::move(system_state_manager));
auto result = service::Connect<fpm::DriverManagerRegistration>();
if (result.is_error()) {
LOGF(ERROR, "Failed to connect to fuchsia.power.manager: %s", result.status_string());
completion(result.error_value());
return;
}
RegisterWithPowerManager(std::move(*result), std::move(system_state_endpoints->client),
std::move(devfs), std::move(completion));
}
void Coordinator::RegisterWithPowerManager(
fidl::ClientEnd<fpm::DriverManagerRegistration> power_manager,
fidl::ClientEnd<fdm::SystemStateTransition> system_state_transition,
fidl::ClientEnd<fio::Directory> devfs, RegisterWithPowerManagerCompletion completion) {
power_manager_client_.Bind(std::move(power_manager), dispatcher_);
power_manager_client_->Register(std::move(system_state_transition), std::move(devfs))
.ThenExactlyOnce(
[this, completion = std::move(completion)](
fidl::WireUnownedResult<fpm::DriverManagerRegistration::Register>& result) mutable {
if (!result.ok()) {
// In this branch, `this` could be invalidated.
// We cannot use any member variable or member function.
LOGF(INFO, "Failed to register with power_manager: %s\n",
result.error().FormatDescription().c_str());
completion(result.status());
return;
}
if (result->is_error()) {
fpm::wire::RegistrationError err = result->error_value();
if (err == fpm::wire::RegistrationError::kInvalidHandle) {
LOGF(ERROR, "Failed to register with power_manager. Invalid handle.\n");
completion(ZX_ERR_BAD_HANDLE);
return;
}
LOGF(ERROR, "Failed to register with power_manager\n");
completion(ZX_ERR_INTERNAL);
return;
}
LOGF(INFO, "Registered with power manager successfully");
set_power_manager_registered(true);
completion(ZX_OK);
});
}
const Driver* Coordinator::LibnameToDriver(std::string_view libname) const {
for (const auto& drv : drivers_) {
if (libname.compare(drv.libname) == 0) {
return &drv;
}
}
return driver_loader_.LibnameToDriver(libname);
}
zx_status_t Coordinator::LibnameToVmo(const fbl::String& libname, zx::vmo* out_vmo) const {
const Driver* drv = LibnameToDriver(libname);
if (drv == nullptr) {
LOGF(ERROR, "Cannot find driver '%s'", libname.data());
return ZX_ERR_NOT_FOUND;
}
// Check for cached DSO
if (drv->dso_vmo != ZX_HANDLE_INVALID) {
zx_status_t r = drv->dso_vmo.duplicate(
ZX_RIGHTS_BASIC | ZX_RIGHTS_PROPERTY | ZX_RIGHT_READ | ZX_RIGHT_EXECUTE | ZX_RIGHT_MAP,
out_vmo);
if (r != ZX_OK) {
LOGF(ERROR, "Cannot duplicate cached DSO for '%s' '%s'", drv->name.data(), libname.data());
}
return r;
} else {
return load_vmo(libname, out_vmo);
}
}
zx_handle_t get_service_root();
zx_status_t Coordinator::GetTopologicalPath(const fbl::RefPtr<const Device>& dev, char* out,
size_t max) {
// TODO: Remove VLA.
char tmp[max];
char name_buf[fio::wire::kMaxFilename + strlen("dev/")];
char* path = tmp + max - 1;
*path = 0;
size_t total = 1;
fbl::RefPtr<const Device> itr = dev;
while (itr != nullptr) {
if (itr->flags & DEV_CTX_PROXY) {
itr = itr->parent();
}
const char* name;
if (itr->name().compare("root") == 0 && itr->parent() == nullptr) {
name = "dev";
} else if (itr->composite() != nullptr) {
strcpy(name_buf, "dev/");
strncpy(name_buf + strlen("dev/"), itr->name().data(), fio::wire::kMaxFilename);
name_buf[sizeof(name_buf) - 1] = 0;
name = name_buf;
} else {
name = itr->name().data();
}
size_t len = strlen(name) + 1;
if (len > (max - total)) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
memcpy(path - len + 1, name, len - 1);
path -= len;
*path = '/';
total += len;
itr = itr->parent();
}
memcpy(out, path, total);
return ZX_OK;
}
zx_status_t Coordinator::NewDriverHost(const char* name, fbl::RefPtr<DriverHost>* out) {
std::string root_driver_path_arg;
std::vector<const char*> env;
if (driver_host_is_asan()) {
env.push_back(kAsanEnvironment);
}
auto driver_host_env = (*boot_args())->Collect("driver.");
if (!driver_host_env.ok()) {
return driver_host_env.status();
}
std::vector<std::string> strings;
for (auto& entry : driver_host_env.value().results) {
strings.emplace_back(entry.data(), entry.size());
}
// Make the clock backstop boot arg available to drivers that
// deal with time (RTC).
// TODO(fxbug.dev/60668): Remove once UTC time is removed from the kernel.
auto backstop_env = (*boot_args())->GetString("clock.backstop");
if (!backstop_env.ok()) {
return backstop_env.status();
}
auto backstop_env_value = std::move(backstop_env.value().value);
if (!backstop_env_value.is_null()) {
strings.push_back(std::string("clock.backstop=") +
std::string(backstop_env_value.data(), backstop_env_value.size()));
}
for (auto& entry : strings) {
env.push_back(entry.data());
}
if (config_.log_to_debuglog) {
env.push_back("devmgr.log-to-debuglog=true");
}
if (config_.verbose) {
env.push_back("devmgr.verbose=true");
}
root_driver_path_arg = "devmgr.root_driver_path=" + config_.path_prefix + "driver/";
env.push_back(root_driver_path_arg.c_str());
env.push_back(nullptr);
DriverHostConfig config{
.name = name,
.binary = kDriverHostPath,
.env = env.data(),
.job = zx::unowned_job(config_.driver_host_job),
.root_resource = zx::unowned_resource(root_resource()),
.loader_service_connector = &loader_service_connector_,
.fs_provider = config_.fs_provider,
.coordinator = this,
};
fbl::RefPtr<DriverHost> dh;
zx_status_t status = DriverHost::Launch(config, &dh);
if (status != ZX_OK) {
return status;
}
launched_first_driver_host_ = true;
VLOGF(1, "New driver_host %p", dh.get());
*out = std::move(dh);
return ZX_OK;
}
zx_status_t Coordinator::MakeVisible(const fbl::RefPtr<Device>& dev) {
if (dev->state() == Device::State::kDead) {
return ZX_ERR_BAD_STATE;
}
if (dev->state() == Device::State::kInitializing) {
// This should only be called in response to the init hook completing.
return ZX_ERR_BAD_STATE;
}
if (dev->flags & DEV_CTX_INVISIBLE) {
dev->flags &= ~DEV_CTX_INVISIBLE;
devfs_advertise(dev);
zx_status_t r = dev->SignalReadyForBind();
if (r != ZX_OK) {
return r;
}
}
return ZX_OK;
}
// Traverse up the device tree to find the metadata with the matching |type|.
// |buffer| can be nullptr, in which case only the size of the metadata is
// returned. This is used by GetMetadataSize method.
zx_status_t Coordinator::GetMetadata(const fbl::RefPtr<Device>& dev, uint32_t type, void* buffer,
size_t buflen, size_t* size) {
// search dev and its parent devices for a match
fbl::RefPtr<Device> test = dev;
while (true) {
for (const auto& md : test->metadata()) {
if (md.type == type) {
if (buffer != nullptr) {
if (md.length > buflen) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
memcpy(buffer, md.Data(), md.length);
}
*size = md.length;
return ZX_OK;
}
}
if (test->parent() == nullptr) {
break;
}
test = test->parent();
}
// search fragments of composite devices
if (test->composite()) {
for (auto& fragment : test->composite()->bound_fragments()) {
auto dev = fragment.bound_device();
if (dev != nullptr) {
if (GetMetadata(dev, type, buffer, buflen, size) == ZX_OK) {
return ZX_OK;
}
}
}
}
return ZX_ERR_NOT_FOUND;
}
zx_status_t Coordinator::AddMetadata(const fbl::RefPtr<Device>& dev, uint32_t type,
const void* data, uint32_t length) {
std::unique_ptr<Metadata> md;
zx_status_t status = Metadata::Create(length, &md);
if (status != ZX_OK) {
return status;
}
md->type = type;
md->length = length;
memcpy(md->Data(), data, length);
dev->AddMetadata(std::move(md));
return ZX_OK;
}
// Create the proxy node for the given device if it doesn't exist and ensure it
// has a driver_host. If |target_driver_host| is not nullptr and the proxy doesn't have
// a driver_host yet, |target_driver_host| will be used for it. Otherwise a new driver_host
// will be created.
zx_status_t Coordinator::PrepareProxy(const fbl::RefPtr<Device>& dev,
fbl::RefPtr<DriverHost> target_driver_host) {
ZX_ASSERT(!(dev->flags & DEV_CTX_PROXY) && (dev->flags & DEV_CTX_MUST_ISOLATE));
// proxy args are "processname,args"
const char* arg0 = dev->args().data();
const char* arg1 = strchr(arg0, ',');
if (arg1 == nullptr) {
LOGF(ERROR, "Missing proxy arguments, expected '%s,args' (see fxbug.dev/33674)", arg0);
return ZX_ERR_INTERNAL;
}
size_t arg0len = arg1 - arg0;
arg1++;
char driver_hostname[32];
snprintf(driver_hostname, sizeof(driver_hostname), "driver_host:%.*s", (int)arg0len, arg0);
zx_status_t r;
if (dev->proxy() == nullptr && (r = dev->CreateProxy()) != ZX_OK) {
LOGF(ERROR, "Cannot create proxy device '%s': %s", dev->name().data(), zx_status_get_string(r));
return r;
}
// if this device has no driver_host, first instantiate it
if (dev->proxy()->host() == nullptr) {
zx::channel h0, h1;
// the immortal root devices do not provide proxy rpc
bool need_proxy_rpc = !(dev->flags & DEV_CTX_IMMORTAL);
if (need_proxy_rpc || dev == sys_device_) {
// create rpc channel for proxy device to talk to the busdev it proxys
if ((r = zx::channel::create(0, &h0, &h1)) < 0) {
return r;
}
}
if (target_driver_host == nullptr) {
if ((r = NewDriverHost(driver_hostname, &target_driver_host)) < 0) {
LOGF(ERROR, "Failed to create driver_host '%s': %s", driver_hostname,
zx_status_get_string(r));
return r;
}
}
dev->proxy()->set_host(std::move(target_driver_host));
if ((r = CreateProxyDevice(dev->proxy(), dev->proxy()->host(), arg1, std::move(h1))) < 0) {
LOGF(ERROR, "Failed to create proxy device '%s' in driver_host '%s': %s", dev->name().data(),
driver_hostname, zx_status_get_string(r));
return r;
}
if (need_proxy_rpc) {
if (auto result = dev->device_controller()->ConnectProxy(std::move(h0)); !result.ok()) {
LOGF(ERROR, "Failed to connect to proxy device '%s' in driver_host '%s': %s",
dev->name().data(), driver_hostname, zx_status_get_string(result.status()));
}
}
if (dev == sys_device_) {
if ((r = fdio_service_connect(kItemsPath, h0.release())) != ZX_OK) {
LOGF(ERROR, "Failed to connect to %s: %s", kItemsPath, zx_status_get_string(r));
}
}
zx::channel client_remote = dev->take_client_remote();
if (client_remote.is_valid()) {
if ((r = devfs_connect(dev->proxy().get(),
fidl::ServerEnd<fio::Node>(std::move(client_remote)))) != ZX_OK) {
LOGF(ERROR, "Failed to connect to service from proxy device '%s' in driver_host '%s': %s",
dev->name().data(), driver_hostname, zx_status_get_string(r));
}
}
}
return ZX_OK;
}
zx_status_t Coordinator::PrepareNewProxy(const fbl::RefPtr<Device>& dev,
fbl::RefPtr<DriverHost> target_driver_host) {
zx_status_t status;
if (dev->new_proxy() == nullptr && (status = dev->CreateNewProxy()) != ZX_OK) {
LOGF(ERROR, "Cannot create new proxy device '%s': %s", dev->name().data(),
zx_status_get_string(status));
return status;
}
char driver_hostname[32];
snprintf(driver_hostname, sizeof(driver_hostname), "driver_host:%.*s",
static_cast<int>(dev->name().size()), dev->name().data());
if (target_driver_host == nullptr) {
if (status = NewDriverHost(driver_hostname, &target_driver_host); status != ZX_OK) {
LOGF(ERROR, "Failed to create driver_host '%s': %s", driver_hostname,
zx_status_get_string(status));
return status;
}
}
dev->new_proxy()->set_host(std::move(target_driver_host));
if (status = CreateNewProxyDevice(dev->new_proxy(), dev->new_proxy()->host(),
dev->take_outgoing_dir());
status != ZX_OK) {
LOGF(ERROR, "Failed to create proxy device '%s' in driver_host '%s': %s", dev->name().data(),
driver_hostname, zx_status_get_string(status));
return status;
}
return ZX_OK;
}
zx_status_t Coordinator::AttemptBind(const Driver* drv, const fbl::RefPtr<Device>& dev) {
if (!driver_host_is_asan() && drv->flags & ZIRCON_DRIVER_NOTE_FLAG_ASAN) {
LOGF(ERROR, "%s (%s) requires ASAN, but we are not in an ASAN environment", drv->libname.data(),
drv->name.data());
return ZX_ERR_BAD_STATE;
}
// cannot bind driver to already bound device
if (dev->IsAlreadyBound()) {
return ZX_ERR_ALREADY_BOUND;
}
if (!(dev->flags & DEV_CTX_MUST_ISOLATE)) {
VLOGF(1, "Binding driver to %s in same driver host as parent", dev->name().data());
// non-busdev is pretty simple
if (dev->host() == nullptr) {
LOGF(ERROR, "Cannot bind to device '%s', it has no driver_host", dev->name().data());
return ZX_ERR_BAD_STATE;
}
return BindDriverToDevice(dev, drv->libname.c_str());
}
zx_status_t status;
if (dev->has_outgoing_directory()) {
VLOGF(1, "Preparing new proxy for %s", dev->name().data());
auto target_driver_host = (dev->flags & DEV_CTX_MUST_ISOLATE) ? nullptr : dev->host();
status = PrepareNewProxy(dev, target_driver_host);
if (status != ZX_OK) {
return status;
}
status = BindDriverToDevice(dev->new_proxy(), drv->libname.c_str());
} else {
VLOGF(1, "Preparing old proxy for %s", dev->name().data());
status = PrepareProxy(dev, nullptr /* target_driver_host */);
if (status != ZX_OK) {
return status;
}
status = BindDriverToDevice(dev->proxy(), drv->libname.c_str());
}
// TODO(swetland): arrange to mark us unbound when the proxy (or its driver_host) goes away
if ((status == ZX_OK) && !(dev->flags & DEV_CTX_MULTI_BIND)) {
dev->flags |= DEV_CTX_BOUND;
}
return status;
}
zx_status_t Coordinator::SetMexecZbis(zx::vmo kernel_zbi, zx::vmo data_zbi) {
if (!kernel_zbi.is_valid() || !data_zbi.is_valid()) {
return ZX_ERR_INVALID_ARGS;
}
if (zx_status_t status = mexec::PrepareDataZbi(root_resource().borrow(), data_zbi.borrow());
status != ZX_OK) {
LOGF(ERROR, "Failed to prepare mexec data ZBI: %s", zx_status_get_string(status));
return status;
}
fidl::WireSyncClient<fuchsia_boot::Items> items;
if (auto result = service::Connect<fuchsia_boot::Items>(); result.is_error()) {
LOGF(ERROR, "Failed to connect to fuchsia.boot::Items: %s", result.status_string());
return result.error_value();
} else {
items = fidl::BindSyncClient(std::move(result).value());
}
// Driver metadata that the driver framework generally expects to be present.
constexpr std::array kItemsToAppend{ZBI_TYPE_DRV_MAC_ADDRESS, ZBI_TYPE_DRV_PARTITION_MAP,
ZBI_TYPE_DRV_BOARD_PRIVATE, ZBI_TYPE_DRV_BOARD_INFO};
zbitl::Image data_image{data_zbi.borrow()};
for (uint32_t type : kItemsToAppend) {
std::string_view name = zbitl::TypeName(type);
// TODO(fxbug.dev/102804): Use a method that returns all matching items of
// a given type instead of guessing possible `extra` values.
for (uint32_t extra : std::array{0, 1, 2}) {
fsl::SizedVmo payload;
if (auto result = items->Get(type, extra); !result.ok()) {
return result.status();
} else if (!result.value().payload.is_valid()) {
// Absence is signified with an empty result value.
LOGF(INFO, "No %.*s item (%#xu) present to append to mexec data ZBI",
static_cast<int>(name.size()), name.data(), type);
continue;
} else {
payload = {std::move(result.value().payload), result.value().length};
}
std::vector<char> contents;
if (!fsl::VectorFromVmo(payload, &contents)) {
LOGF(ERROR, "Failed to read contents of %.*s item (%#xu)", static_cast<int>(name.size()),
name.data(), type);
return ZX_ERR_INTERNAL;
}
if (auto result = data_image.Append(zbi_header_t{.type = type, .extra = extra},
zbitl::AsBytes(contents));
result.is_error()) {
LOGF(ERROR, "Failed to append %.*s item (%#xu) to mexec data ZBI: %s",
static_cast<int>(name.size()), name.data(), type,
zbitl::ViewErrorString(result.error_value()).c_str());
return ZX_ERR_INTERNAL;
}
}
}
mexec_kernel_zbi_ = std::move(kernel_zbi);
mexec_data_zbi_ = std::move(data_zbi);
return ZX_OK;
}
// DriverAdded is called when a driver is added after the
// devcoordinator has started. The driver is added to the new-drivers
// list and work is queued to process it.
void Coordinator::DriverAdded(Driver* drv, const char* version) {
fbl::DoublyLinkedList<std::unique_ptr<Driver>> driver_list;
driver_list.push_back(std::unique_ptr<Driver>(drv));
async::PostTask(dispatcher_, [this, driver_list = std::move(driver_list)]() mutable {
AddAndBindDrivers(std::move(driver_list));
});
}
// DriverAddedInit is called from driver enumeration during
// startup and before the devcoordinator starts running. Enumerated
// drivers are added directly to the all-drivers or fallback list.
//
// TODO: fancier priorities
void Coordinator::DriverAddedInit(Driver* drv, const char* version) {
auto driver = std::unique_ptr<Driver>(drv);
if (driver->fallback) {
// fallback driver, load only if all else fails
fallback_drivers_.push_front(std::move(driver));
} else if (version[0] == '!') {
// debugging / development hack
// prioritize drivers with version "!..." over others
drivers_.push_front(std::move(driver));
} else {
drivers_.push_back(std::move(driver));
}
}
zx_status_t Coordinator::AddDeviceGroup(
const fbl::RefPtr<Device>& dev, std::string_view name,
fuchsia_device_manager::wire::DeviceGroupDescriptor group_desc) {
if (group_desc.fragments.count() == 0) {
return ZX_ERR_INVALID_ARGS;
}
char device_path[fdm::wire::kDevicePathMax + 1];
zx_status_t status = GetTopologicalPath(dev, device_path, sizeof(device_path));
if (status != ZX_OK) {
return status;
}
// Append the device name to the path.
auto topological_path = std::string(device_path) + "/";
topological_path.append(name);
status = driver_loader_.AddDeviceGroup(topological_path, group_desc.fragments);
if (status != ZX_OK) {
LOGF(ERROR, "Failed to add device group to the driver index.\n");
return status;
}
status = bind_driver_manager_->AddDeviceGroup(topological_path, name, group_desc);
if (status != ZX_OK) {
LOGF(ERROR, "Failed to add device group to the bind driver manager.\n");
return status;
}
return ZX_OK;
}
zx_status_t Coordinator::BindDriver(Driver* drv) {
if (!running_) {
return ZX_ERR_UNAVAILABLE;
}
for (auto& dev : device_manager_->devices()) {
zx_status_t status =
bind_driver_manager_->MatchAndBind(fbl::RefPtr(&dev), drv, true /* autobind */);
if (status == ZX_ERR_NEXT || status == ZX_ERR_ALREADY_BOUND) {
continue;
}
if (status != ZX_OK) {
return status;
}
}
return ZX_OK;
}
void Coordinator::AddAndBindDrivers(fbl::DoublyLinkedList<std::unique_ptr<Driver>> drivers) {
std::unique_ptr<Driver> driver;
while ((driver = drivers.pop_front()) != nullptr) {
Driver* driver_ptr = driver.get();
drivers_.push_back(std::move(driver));
zx_status_t status = BindDriver(driver_ptr);
if (status != ZX_OK && status != ZX_ERR_UNAVAILABLE) {
LOGF(ERROR, "Failed to bind driver '%s': %s", driver_ptr->name.data(),
zx_status_get_string(status));
}
}
}
void Coordinator::StartLoadingNonBootDrivers() { driver_loader_.StartSystemLoadingThread(this); }
void Coordinator::BindFallbackDrivers() {
for (auto& driver : fallback_drivers_) {
LOGF(INFO, "Fallback driver '%s' is available", driver.name.data());
}
AddAndBindDrivers(std::move(fallback_drivers_));
}
void Coordinator::GetDriverInfo(GetDriverInfoRequestView request,
GetDriverInfoCompleter::Sync& completer) {
std::vector<const Driver*> driver_list;
if (request->driver_filter.empty()) {
for (const auto& driver : drivers()) {
driver_list.push_back(&driver);
}
} else {
for (const auto& d : request->driver_filter) {
std::string_view driver_path(d.data(), d.size());
for (const auto& drv : drivers()) {
if (driver_path.compare(drv.libname) == 0) {
driver_list.push_back(&drv);
break;
}
}
}
}
auto arena = std::make_unique<fidl::Arena<512>>();
auto result = ::GetDriverInfo(*arena, driver_list);
if (result.is_error()) {
request->iterator.Close(result.status_value());
return;
}
auto index_result = driver_loader_.GetDriverInfo(*arena, request->driver_filter);
if (!index_result.is_error()) {
result->reserve(result->size() + index_result->size());
result->insert(result->end(), index_result->begin(), index_result->end());
} else {
LOGF(ERROR, "Failed to call index: %s\n", zx_status_get_string(index_result.status_value()));
}
// If we have driver filters check that we found one driver per filter.
if (!request->driver_filter.empty() && (result->size() != request->driver_filter.count())) {
request->iterator.Close(ZX_ERR_NOT_FOUND);
return;
}
auto iterator = std::make_unique<DriverInfoIterator>(std::move(arena), std::move(*result));
fidl::BindServer(dispatcher(), std::move(request->iterator), std::move(iterator));
}
void Coordinator::Register(RegisterRequestView request, RegisterCompleter::Sync& completer) {
std::string driver_url_str(request->package_url.url.data(), request->package_url.url.size());
zx_status_t status = LoadEphemeralDriver(&package_resolver_, driver_url_str);
if (status != ZX_OK) {
LOGF(ERROR, "Could not load '%s'", driver_url_str.c_str());
completer.ReplyError(status);
return;
}
LOGF(INFO, "Loaded driver '%s'", driver_url_str.c_str());
completer.ReplySuccess();
}
zx_status_t Coordinator::LoadEphemeralDriver(internal::PackageResolverInterface* resolver,
const std::string& package_url) {
ZX_ASSERT(config_.enable_ephemeral);
auto result = resolver->FetchDriver(package_url);
if (!result.is_ok()) {
return result.status_value();
}
fbl::DoublyLinkedList<std::unique_ptr<Driver>> driver_list;
driver_list.push_back(std::move(result.value()));
async::PostTask(dispatcher_, [this, driver_list = std::move(driver_list)]() mutable {
AddAndBindDrivers(std::move(driver_list));
});
return ZX_OK;
}
void Coordinator::GetDeviceInfo(GetDeviceInfoRequestView request,
GetDeviceInfoCompleter::Sync& completer) {
std::vector<fbl::RefPtr<Device>> device_list;
if (request->device_filter.empty()) {
for (auto& device : device_manager_->devices()) {
device_list.push_back(fbl::RefPtr(&device));
}
} else {
for (const auto& device_path : request->device_filter) {
fbl::RefPtr<Device> device;
std::string path(device_path.data(), device_path.size());
zx_status_t status = devfs_walk(root_device_->devnode(), path.c_str(), &device);
if (status != ZX_OK) {
request->iterator.Close(status);
return;
}
device_list.push_back(std::move(device));
}
}
auto arena = std::make_unique<fidl::Arena<512>>();
auto result = ::GetDeviceInfo(*arena, device_list);
if (result.is_error()) {
request->iterator.Close(result.status_value());
return;
}
auto iterator = std::make_unique<DeviceInfoIterator>(std::move(arena), std::move(*result));
fidl::BindServer(dispatcher(), std::move(request->iterator), std::move(iterator),
[](auto* server, fidl::UnbindInfo info, auto channel) {
if (!info.is_peer_closed()) {
LOGF(WARNING, "Closed DeviceInfoIterator: %s", info.lossy_description());
}
});
}
void Coordinator::BindAllUnboundNodes(BindAllUnboundNodesRequestView request,
BindAllUnboundNodesCompleter::Sync& completer) {
LOGF(WARNING, "BindAllUnboundNodes is only supported in DFv2.");
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void Coordinator::UnregisterSystemStorageForShutdown(
UnregisterSystemStorageForShutdownRequestView request,
UnregisterSystemStorageForShutdownCompleter::Sync& completer) {
suspend_resume_manager_->suspend_handler().UnregisterSystemStorageForShutdown(
[completer = completer.ToAsync()](zx_status_t status) mutable { completer.Reply(status); });
}
zx::status<> Coordinator::PublishDriverDevelopmentService(
const fbl::RefPtr<fs::PseudoDir>& svc_dir) {
const auto driver_dev = [this](fidl::ServerEnd<fdd::DriverDevelopment> request) {
fidl::BindServer<fidl::WireServer<fdd::DriverDevelopment>>(
dispatcher_, std::move(request), this,
[](fidl::WireServer<fdd::DriverDevelopment>* self, fidl::UnbindInfo info,
fidl::ServerEnd<fdd::DriverDevelopment> server_end) {
if (info.is_user_initiated()) {
return;
}
if (info.is_peer_closed()) {
// For this development protocol, the client is free to disconnect
// at any time.
return;
}
LOGF(ERROR, "Error serving '%s': %s",
fidl::DiscoverableProtocolName<fdd::DriverDevelopment>,
info.FormatDescription().c_str());
});
return ZX_OK;
};
zx_status_t status = svc_dir->AddEntry(fidl::DiscoverableProtocolName<fdd::DriverDevelopment>,
fbl::MakeRefCounted<fs::Service>(driver_dev));
return zx::make_status(status);
}
zx::status<> Coordinator::PublishDriverRegistrarService(const fbl::RefPtr<fs::PseudoDir>& svc_dir) {
const auto driver_registrar = [this](fidl::ServerEnd<fdr::DriverRegistrar> request) {
driver_registrar_binding_ = fidl::BindServer<fidl::WireServer<fdr::DriverRegistrar>>(
dispatcher_, std::move(request), this);
return ZX_OK;
};
zx_status_t status = svc_dir->AddEntry(fidl::DiscoverableProtocolName<fdr::DriverRegistrar>,
fbl::MakeRefCounted<fs::Service>(driver_registrar));
return zx::make_status(status);
}
zx_status_t Coordinator::InitOutgoingServices(const fbl::RefPtr<fs::PseudoDir>& svc_dir) {
const auto admin = [this](fidl::ServerEnd<fdm::Administrator> request) {
fidl::BindServer<fidl::WireServer<fdm::Administrator>>(dispatcher_, std::move(request), this);
return ZX_OK;
};
zx_status_t status = svc_dir->AddEntry(fidl::DiscoverableProtocolName<fdm::Administrator>,
fbl::MakeRefCounted<fs::Service>(admin));
if (status != ZX_OK) {
return status;
}
const auto system_state_manager_register =
[this](fidl::ServerEnd<fdm::SystemStateTransition> request) {
auto status = fidl::BindSingleInFlightOnly<fidl::WireServer<fdm::SystemStateTransition>>(
dispatcher_, std::move(request), std::make_unique<SystemStateManager>(this));
if (status != ZX_OK) {
LOGF(ERROR, "Failed to bind to client channel for '%s': %s",
fidl::DiscoverableProtocolName<fdm::SystemStateTransition>,
zx_status_get_string(status));
}
return status;
};
status = svc_dir->AddEntry(fidl::DiscoverableProtocolName<fdm::SystemStateTransition>,
fbl::MakeRefCounted<fs::Service>(system_state_manager_register));
if (status != ZX_OK) {
LOGF(ERROR, "Failed to add entry in service directory for '%s': %s",
fidl::DiscoverableProtocolName<fdm::SystemStateTransition>, zx_status_get_string(status));
return status;
}
const auto debug = [this](fidl::ServerEnd<fdm::DebugDumper> request) {
fidl::BindServer<fidl::WireServer<fdm::DebugDumper>>(dispatcher_, std::move(request),
debug_dump_.get());
return ZX_OK;
};
return svc_dir->AddEntry(fidl::DiscoverableProtocolName<fdm::DebugDumper>,
fbl::MakeRefCounted<fs::Service>(debug));
}
std::string Coordinator::GetFragmentDriverUrl() const { return "#driver/fragment.so"; }
void Coordinator::RestartDriverHosts(RestartDriverHostsRequestView request,
RestartDriverHostsCompleter::Sync& completer) {
std::string_view driver_path(request->driver_path.data(), request->driver_path.size());
// Find devices containing the driver.
uint32_t count = 0;
for (auto& dev : device_manager_->devices()) {
// Call remove on the device's driver host if it contains the driver.
if (dev.libname().compare(driver_path) == 0) {
LOGF(INFO, "Device %s found in restart driver hosts.", dev.name().data());
LOGF(INFO, "Shutting down host: %ld.", dev.host()->koid());
// Unbind and Remove all the devices in the Driver Host.
device_manager_->ScheduleUnbindRemoveAllDevices(dev.host());
count++;
}
}
completer.ReplySuccess(count);
}