blob: 2577213b2322427a3c8c62c5a0250969295868e6 [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.
#ifndef SRC_DEVICES_BIN_DRIVER_MANAGER_COORDINATOR_H_
#define SRC_DEVICES_BIN_DRIVER_MANAGER_COORDINATOR_H_
#include <fuchsia/boot/llcpp/fidl.h>
#include <fuchsia/fshost/llcpp/fidl.h>
#include <fuchsia/power/manager/llcpp/fidl.h>
#include <lib/async/cpp/wait.h>
#include <lib/fidl/llcpp/server.h>
#include <lib/svc/outgoing.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <lib/zx/job.h>
#include <lib/zx/process.h>
#include <lib/zx/status.h>
#include <lib/zx/vmo.h>
#include <memory>
#include <utility>
#include <ddk/binding.h>
#include <ddk/device.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/string.h>
#include <fbl/vector.h>
#include "composite_device.h"
#include "devfs.h"
#include "device.h"
#include "driver.h"
#include "driver_host.h"
#include "fbl/auto_lock.h"
#include "fuchsia/device/manager/llcpp/fidl.h"
#include "fuchsia/hardware/power/statecontrol/llcpp/fidl.h"
#include "init_task.h"
#include "inspect.h"
#include "metadata.h"
#include "resume_task.h"
#include "suspend_task.h"
#include "system_state_manager.h"
#include "unbind_task.h"
#include "vmo_writer.h"
namespace power_fidl = llcpp::fuchsia::hardware::power;
using power_fidl::statecontrol::SystemPowerState;
namespace device_manager_fidl = llcpp::fuchsia::device::manager;
namespace power_manager_fidl = llcpp::fuchsia::power::manager;
class DriverHostLoaderService;
class FsProvider;
class SystemStateManager;
constexpr zx::duration kDefaultResumeTimeout = zx::sec(30);
constexpr zx::duration kDefaultSuspendTimeout = zx::sec(30);
class SuspendContext {
public:
enum class Flags : uint32_t {
kRunning = 0u,
kSuspend = 1u,
};
SuspendContext() = default;
SuspendContext(Flags flags, uint32_t sflags) : flags_(flags), sflags_(sflags) {}
~SuspendContext() {}
SuspendContext(SuspendContext&&) = default;
SuspendContext& operator=(SuspendContext&&) = default;
void set_task(fbl::RefPtr<SuspendTask> task) { task_ = std::move(task); }
const SuspendTask* task() const { return task_.get(); }
Flags flags() const { return flags_; }
void set_flags(Flags flags) { flags_ = flags; }
uint32_t sflags() const { return sflags_; }
async::TaskClosure* watchdog_task() const { return suspend_watchdog_task_.get(); }
void set_suspend_watchdog_task(std::unique_ptr<async::TaskClosure> watchdog_task) {
suspend_watchdog_task_ = std::move(watchdog_task);
}
private:
fbl::RefPtr<SuspendTask> task_;
std::unique_ptr<async::TaskClosure> suspend_watchdog_task_;
Flags flags_ = Flags::kRunning;
// suspend flags
uint32_t sflags_ = 0u;
};
// Tracks the global resume state that is currently in progress.
class ResumeContext {
public:
enum class Flags : uint32_t {
kResume = 0u,
kSuspended = 1u,
};
ResumeContext() = default;
ResumeContext(Flags flags, SystemPowerState resume_state)
: target_state_(resume_state), flags_(flags) {}
~ResumeContext() {}
ResumeContext(ResumeContext&&) = default;
ResumeContext& operator=(ResumeContext&&) = default;
Flags flags() const { return flags_; }
void set_flags(Flags flags) { flags_ = flags; }
void push_pending_task(fbl::RefPtr<ResumeTask> task) {
pending_resume_tasks_.push_back(std::move(task));
}
void push_completed_task(fbl::RefPtr<ResumeTask> task) {
completed_resume_tasks_.push_back(std::move(task));
}
bool pending_tasks_is_empty() { return pending_resume_tasks_.is_empty(); }
bool completed_tasks_is_empty() { return completed_resume_tasks_.is_empty(); }
std::optional<fbl::RefPtr<ResumeTask>> take_pending_task(Device& dev) {
for (size_t i = 0; i < pending_resume_tasks_.size(); i++) {
if (&pending_resume_tasks_[i]->device() == &dev) {
auto task = pending_resume_tasks_.erase(i);
return std::move(task);
}
}
return {};
}
void reset_completed_tasks() { completed_resume_tasks_.reset(); }
SystemPowerState target_state() const { return target_state_; }
private:
fbl::Vector<fbl::RefPtr<ResumeTask>> pending_resume_tasks_;
fbl::Vector<fbl::RefPtr<ResumeTask>> completed_resume_tasks_;
SystemPowerState target_state_;
Flags flags_ = Flags::kSuspended;
};
using ResumeCallback = std::function<void(zx_status_t)>;
using SuspendCallback = fit::function<void(zx_status_t)>;
// Values parsed out of argv. All paths described below are absolute paths.
struct DevmgrArgs {
// Load drivers from these directories. If this is empty, the default will
// be used.
fbl::Vector<std::string> driver_search_paths;
// Load the drivers with these paths. The specified drivers do not need to
// be in directories in |driver_search_paths|.
fbl::Vector<const char*> load_drivers;
// Use this driver as the sys_device driver. If nullptr, the default will
// be used.
std::string sys_device_driver;
// Select whether to launch a new svchost process, or to use the /svc provided through the
// namespace when launching subprocesses (only used in integration tests).
bool start_svchost = true;
// Disables the block watcher if set to true. This can be used for testing purposes,
// where it is not necessary to have the block watcher running.
bool disable_block_watcher = false;
// Disables the netsvc if set to true. This can be used for testing purposes,
// where it is not necessary to have the netsvc running.
bool disable_netsvc = false;
// Connect the stdout and stderr file descriptors for this program to a
// debuglog handle acquired with fuchsia.boot.WriteOnlyLog.
bool log_to_debuglog = false;
// Path prefix for binaries/drivers/libraries etc.
std::string path_prefix = "/boot/";
// Use the default loader rather than the one provided by fshost.
bool use_default_loader = false;
bool no_exit_after_suspend = false;
};
struct CoordinatorConfig {
// Initial root resource from the kernel.
zx::resource root_resource;
// Job for all driver_hosts.
zx::job driver_host_job;
// Event that is signaled by the kernel in OOM situation.
zx::event oom_event;
// Client for the Arguments service.
llcpp::fuchsia::boot::Arguments::SyncClient* boot_args;
// If true, netsvc is disabled and will not start.
bool disable_netsvc = false;
// Whether we require /system.
bool require_system = false;
// Whether we require ASan drivers.
bool asan_drivers = false;
// Whether to reboot the device when suspend does not finish on time.
bool suspend_fallback = false;
// Whether to output logs to debuglog.
bool log_to_debuglog = false;
// Whether to enable verbose logging.
bool verbose = false;
// Timeout for system wide suspend
zx::duration suspend_timeout = kDefaultSuspendTimeout;
// Timeout for system wide resume
zx::duration resume_timeout = kDefaultResumeTimeout;
// System will be transitioned to this system power state during
// component shutdown.
power_fidl::statecontrol::SystemPowerState default_shutdown_system_state =
power_fidl::statecontrol::SystemPowerState::REBOOT;
// Something to clone a handle from the environment to pass to a Devhost.
FsProvider* fs_provider = nullptr;
// The path prefix to find binaries, drivers, etc. Typically this is "/boot/", but in test
// environments this might be different.
std::string path_prefix = "/boot/";
};
struct SuspendCallbackInfo : public fbl::RefCounted<SuspendCallbackInfo> {
SuspendCallbackInfo(SuspendCallback callback) : callback(std::move(callback)) {}
SuspendCallback callback;
};
class Coordinator : public device_manager_fidl::BindDebugger::Interface {
public:
Coordinator(const Coordinator&) = delete;
Coordinator& operator=(const Coordinator&) = delete;
Coordinator(Coordinator&&) = delete;
Coordinator& operator=(Coordinator&&) = delete;
Coordinator(CoordinatorConfig config, async_dispatcher_t* dispatcher);
~Coordinator();
zx_status_t InitOutgoingServices(const fbl::RefPtr<fs::PseudoDir>& svc_dir);
zx_status_t InitCoreDevices(std::string_view sys_device_driver);
zx::status<> InitInspect();
bool InSuspend() const;
bool InResume() const;
zx_status_t ScanSystemDrivers();
void BindDrivers();
void UseFallbackDrivers();
void DriverAdded(Driver* drv, const char* version);
void DriverAddedInit(Driver* drv, const char* version);
zx_status_t LibnameToVmo(const fbl::String& libname, zx::vmo* out_vmo) const;
const Driver* LibnameToDriver(const fbl::StringPiece& libname) const;
// Function that is invoked to request a driver try to bind to a device
using AttemptBindFunc =
fit::function<zx_status_t(const Driver* drv, const fbl::RefPtr<Device>& dev)>;
// Attempts to bind the given driver to the given device. Returns ZX_OK on
// success, ZX_ERR_ALREADY_BOUND if there is a driver bound to the device
// and the device is not allowed to be bound multiple times, ZX_ERR_NEXT if
// the driver is not capable of binding to the device, and a different error
// if the driver was capable of binding but failed to bind.
zx_status_t BindDriverToDevice(const fbl::RefPtr<Device>& dev, const Driver* drv, bool autobind) {
return BindDriverToDevice(dev, drv, autobind,
fit::bind_member(this, &Coordinator::AttemptBind));
}
// The same as above, but the given function is called to perform the
// bind attempt.
zx_status_t BindDriverToDevice(const fbl::RefPtr<Device>& dev, const Driver* drv, bool autobind,
const AttemptBindFunc& attempt_bind);
// Used to implement fuchsia::device::manager::Coordinator.
// TODO(fxbug.dev/43370): remove |always_init| once init tasks can be enabled for all devices.
zx_status_t AddDevice(const fbl::RefPtr<Device>& parent, zx::channel device_controller,
zx::channel coordinator,
const llcpp::fuchsia::device::manager::DeviceProperty* props_data,
size_t props_count, fbl::StringPiece name, uint32_t protocol_id,
fbl::StringPiece driver_path, fbl::StringPiece args, bool invisible,
bool skip_autobind, bool has_init, bool always_init, zx::vmo inspect,
zx::channel client_remote, fbl::RefPtr<Device>* new_device);
// Begin scheduling for removal of the device and unbinding of its children.
void ScheduleRemove(const fbl::RefPtr<Device>& dev);
// This is for scheduling the initial unbind task as a result of a driver_host's |ScheduleRemove|
// request.
// If |do_unbind| is true, unbinding is also requested for |dev|.
void ScheduleDriverHostRequestedRemove(const fbl::RefPtr<Device>& dev, bool do_unbind = false);
void ScheduleDriverHostRequestedUnbindChildren(const fbl::RefPtr<Device>& parent);
zx_status_t RemoveDevice(const fbl::RefPtr<Device>& dev, bool forced);
zx_status_t MakeVisible(const fbl::RefPtr<Device>& dev);
// Try binding a driver to the device. Returns ZX_ERR_ALREADY_BOUND if there
// is a driver bound to the device and the device is not allowed to be bound multiple times.
zx_status_t BindDevice(const fbl::RefPtr<Device>& dev, fbl::StringPiece drvlibname,
bool new_device);
zx_status_t GetTopologicalPath(const fbl::RefPtr<const Device>& dev, char* out, size_t max) const;
zx_status_t LoadFirmware(const fbl::RefPtr<Device>& dev, const char* path, zx::vmo* vmo,
size_t* size);
zx_status_t GetMetadata(const fbl::RefPtr<Device>& dev, uint32_t type, void* buffer,
size_t buflen, size_t* size);
zx_status_t GetMetadataSize(const fbl::RefPtr<Device>& dev, uint32_t type, size_t* size) {
return GetMetadata(dev, type, nullptr, 0, size);
}
zx_status_t AddMetadata(const fbl::RefPtr<Device>& dev, uint32_t type, const void* data,
uint32_t length);
zx_status_t PublishMetadata(const fbl::RefPtr<Device>& dev, const char* path, uint32_t type,
const void* data, uint32_t length);
zx_status_t AddCompositeDevice(
const fbl::RefPtr<Device>& dev, fbl::StringPiece name,
llcpp::fuchsia::device::manager::CompositeDeviceDescriptor comp_desc);
void DmMexec(zx::vmo kernel, zx::vmo bootdata);
void HandleNewDevice(const fbl::RefPtr<Device>& dev);
zx_status_t PrepareProxy(const fbl::RefPtr<Device>& dev,
fbl::RefPtr<DriverHost> target_driver_host);
void DumpState(VmoWriter* vmo) const;
async_dispatcher_t* dispatcher() const { return dispatcher_; }
const zx::resource& root_resource() const { return config_.root_resource; }
llcpp::fuchsia::boot::Arguments::SyncClient* boot_args() const { return config_.boot_args; }
bool disable_netsvc() const { return config_.disable_netsvc; }
bool require_system() const { return config_.require_system; }
bool suspend_fallback() const { return config_.suspend_fallback; }
power_fidl::statecontrol::SystemPowerState shutdown_system_state() const {
return shutdown_system_state_;
}
power_fidl::statecontrol::SystemPowerState default_shutdown_system_state() const {
return config_.default_shutdown_system_state;
}
void set_shutdown_system_state(power_fidl::statecontrol::SystemPowerState state) {
shutdown_system_state_ = state;
}
void set_running(bool running) { running_ = running; }
bool system_available() const { return system_available_; }
void set_system_available(bool system_available) { system_available_ = system_available; }
void set_power_manager_registered(bool registered) { power_manager_registered_ = registered; }
bool power_manager_registered() { return power_manager_registered_; }
bool system_loaded() const { return system_loaded_; }
void set_loader_service_connector(LoaderServiceConnector loader_service_connector) {
loader_service_connector_ = std::move(loader_service_connector);
}
void set_system_state_manager(std::unique_ptr<SystemStateManager> system_state_mgr) {
system_state_manager_ = std::move(system_state_mgr);
}
fbl::DoublyLinkedList<std::unique_ptr<Driver>>& drivers() { return drivers_; }
const fbl::DoublyLinkedList<std::unique_ptr<Driver>>& drivers() const { return drivers_; }
fbl::TaggedDoublyLinkedList<fbl::RefPtr<Device>, Device::AllDevicesListTag>& devices() {
return devices_;
}
const fbl::TaggedDoublyLinkedList<fbl::RefPtr<Device>, Device::AllDevicesListTag>& devices()
const {
return devices_;
}
void AppendPublishedMetadata(std::unique_ptr<Metadata> metadata) {
published_metadata_.push_back(std::move(metadata));
}
const fbl::RefPtr<Device>& root_device() { return root_device_; }
const fbl::RefPtr<Device>& misc_device() { return misc_device_; }
const fbl::RefPtr<Device>& sys_device() { return sys_device_; }
const fbl::RefPtr<Device>& test_device() { return test_device_; }
void Suspend(uint32_t flags);
void Suspend(SuspendContext ctx, fit::function<void(zx_status_t)> callback);
void Resume(
SystemPowerState target_state, ResumeCallback callback = [](zx_status_t) {});
SuspendContext& suspend_context() { return suspend_context_; }
const SuspendContext& suspend_context() const { return suspend_context_; }
ResumeContext& resume_context() { return resume_context_; }
const ResumeContext& resume_context() const { return resume_context_; }
zx_status_t BindFidlServiceProxy(zx::channel listen_on);
const Driver* fragment_driver() const { return fragment_driver_; }
InspectManager& inspect_manager() { return inspect_manager_; }
// This method is public only for the test suite.
zx_status_t BindDriver(Driver* drv, const AttemptBindFunc& attempt_bind);
uint32_t GetSuspendFlagsFromSystemPowerState(power_fidl::statecontrol::SystemPowerState state);
// These methods are used by the DriverHost class to register in the coordinator's bookkeeping
void RegisterDriverHost(DriverHost* dh) { driver_hosts_.push_back(dh); }
void UnregisterDriverHost(DriverHost* dh) { driver_hosts_.erase(*dh); }
// Returns path to driver that should be bound to fragments of composite devices.
std::string GetFragmentDriverPath() const;
zx_status_t RegisterWithPowerManager(zx::channel devfs_handle);
zx_status_t RegisterWithPowerManager(zx::channel power_manager_client,
zx::channel system_state_transition_client,
zx::channel devfs_handle);
protected:
std::unique_ptr<llcpp::fuchsia::fshost::Admin::SyncClient> fshost_admin_client_;
private:
CoordinatorConfig config_;
async_dispatcher_t* const dispatcher_;
bool running_ = false;
bool launched_first_driver_host_ = false;
bool system_available_ = false;
bool system_loaded_ = false;
bool power_manager_registered_ = false;
LoaderServiceConnector loader_service_connector_;
fidl::Client<power_manager_fidl::DriverManagerRegistration> power_manager_client_;
// All Drivers
fbl::DoublyLinkedList<std::unique_ptr<Driver>> drivers_;
// Drivers to try last
fbl::DoublyLinkedList<std::unique_ptr<Driver>> fallback_drivers_;
// List of drivers loaded from /system by system_driver_loader()
fbl::DoublyLinkedList<std::unique_ptr<Driver>> system_drivers_;
// All DriverHosts
fbl::DoublyLinkedList<DriverHost*> driver_hosts_;
// All Devices (excluding static immortal devices)
fbl::TaggedDoublyLinkedList<fbl::RefPtr<Device>, Device::AllDevicesListTag> devices_;
// All composite devices
fbl::DoublyLinkedList<std::unique_ptr<CompositeDevice>> composite_devices_;
fbl::RefPtr<Device> root_device_;
fbl::RefPtr<Device> misc_device_;
fbl::RefPtr<Device> sys_device_;
fbl::RefPtr<Device> test_device_;
SuspendContext suspend_context_ = {};
ResumeContext resume_context_;
InspectManager inspect_manager_;
std::unique_ptr<SystemStateManager> system_state_manager_;
power_fidl::statecontrol::SystemPowerState shutdown_system_state_;
// Bind debugger interface
void GetBindProgram(::fidl::StringView driver_path,
GetBindProgramCompleter::Sync& completer) override;
void GetDeviceProperties(::fidl::StringView name,
GetDevicePropertiesCompleter::Sync& completer) override;
void OnOOMEvent(async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status,
const zx_packet_signal_t* signal);
async::WaitMethod<Coordinator, &Coordinator::OnOOMEvent> wait_on_oom_event_{this};
fbl::DoublyLinkedList<std::unique_ptr<Metadata>> published_metadata_;
// Once the special fragment driver is loaded, this will refer to it. This
// driver is used for binding against fragments of composite devices
const Driver* fragment_driver_ = nullptr;
void DumpDevice(VmoWriter* vmo, const Device* dev, size_t indent) const;
void DumpDeviceProps(VmoWriter* vmo, const Device* dev) const;
void DumpGlobalDeviceProps(VmoWriter* vmo) const;
void DumpDrivers(VmoWriter* vmo) const;
void BuildSuspendList();
void Resume(ResumeContext ctx, std::function<void(zx_status_t)> callback);
std::unique_ptr<Driver> ValidateDriver(std::unique_ptr<Driver> drv);
zx_status_t NewDriverHost(const char* name, fbl::RefPtr<DriverHost>* out);
zx_status_t BindDriver(Driver* drv) {
return BindDriver(drv, fit::bind_member(this, &Coordinator::AttemptBind));
}
zx_status_t AttemptBind(const Driver* drv, const fbl::RefPtr<Device>& dev);
void BindSystemDrivers();
void DriverAddedSys(Driver* drv, const char* version);
zx_status_t GetMetadataRecurse(const fbl::RefPtr<Device>& dev, uint32_t type, void* buffer,
size_t buflen, size_t* size);
// Shut down all filesystems (and fshost itself) by calling
// fuchsia.fshost.Admin.Shutdown(). Note that this is called from multiple
// different locations; during suspension, and in a low-memory situation.
// Currently, both of these calls happen on the same dispatcher thread, but
// consider thread safety when refactoring.
void ShutdownFilesystems();
};
bool driver_is_bindable(const Driver* drv, uint32_t protocol_id,
const fbl::Array<const zx_device_prop_t>& props, bool autobind);
zx_status_t fidl_DirectoryWatch(void* ctx, uint32_t mask, uint32_t options, zx_handle_t raw_watcher,
fidl_txn_t* txn);
#endif // SRC_DEVICES_BIN_DRIVER_MANAGER_COORDINATOR_H_