blob: 71d84f3a87dbaf96d1280a06350f7d6581308a92 [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.
#pragma once
#include <ddk/binding.h>
#include <ddk/device.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/string.h>
#include <fbl/unique_ptr.h>
#include <fbl/vector.h>
#include <fuchsia/device/manager/c/fidl.h>
#include <lib/async/cpp/wait.h>
#include <lib/svc/outgoing.h>
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <lib/zx/job.h>
#include <lib/zx/process.h>
#include <lib/zx/vmo.h>
#include <utility>
#include "boot-args.h"
#include "composite-device.h"
#include "devhost.h"
#include "device.h"
#include "driver.h"
#include "metadata.h"
#include "suspend-task.h"
#include "vmo-writer.h"
namespace devmgr {
class DevhostLoaderService;
class SuspendContext {
public:
enum class Flags : uint32_t {
kRunning = 0u,
kSuspend = 1u,
};
SuspendContext() = default;
SuspendContext(Flags flags, uint32_t sflags, zx::vmo kernel = zx::vmo(),
zx::vmo bootdata = zx::vmo())
: flags_(flags), sflags_(sflags), kernel_(std::move(kernel)),
bootdata_(std::move(bootdata)) {}
~SuspendContext() {}
SuspendContext(SuspendContext&&) = default;
SuspendContext& operator=(SuspendContext&&) = default;
void set_task(fbl::RefPtr<SuspendTask> task) { task_ = std::move(task); }
Flags flags() const { return flags_; }
void set_flags(Flags flags) { flags_ = flags; }
uint32_t sflags() const { return sflags_; }
const zx::vmo& kernel() const { return kernel_; }
const zx::vmo& bootdata() const { return bootdata_; }
private:
fbl::RefPtr<SuspendTask> task_;
Flags flags_ = Flags::kRunning;
// suspend flags
uint32_t sflags_ = 0u;
// mexec arguments
zx::vmo kernel_;
zx::vmo bootdata_;
};
// 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<const char*> 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.
const char* sys_device_driver = nullptr;
// Select whether to launch a new svchost or to just use the system provided
// /svc directory.
bool use_system_svchost = false;
// 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;
};
struct CoordinatorConfig {
// Initial root resource from the kernel.
zx::resource root_resource;
// Job for all devhosts.
zx::job devhost_job;
// Event that controls the fshost.
zx::event fshost_event;
// Async dispatcher for the coordinator.
async_dispatcher_t* dispatcher;
// Boot arguments from the Arguments service.
const devmgr::BootArgs* boot_args;
// If true, netsvc is disabled and will not start.
bool disable_netsvc;
// Whether we require /system.
bool require_system;
// Whether we require ASan drivers.
bool asan_drivers;
// Whether to reboot the device when suspend does not finish on time.
bool suspend_fallback;
// Whether to print out debugging when suspend does not finish on time.
bool suspend_debug;
};
struct CompatibilityTestArgs {
fbl::RefPtr<Device> dev;
char devname[fuchsia_device_manager_DEVICE_NAME_MAX];
};
class Coordinator {
public:
Coordinator(const Coordinator&) = delete;
Coordinator& operator=(const Coordinator&) = delete;
Coordinator(Coordinator&&) = delete;
Coordinator& operator=(Coordinator&&) = delete;
explicit Coordinator(CoordinatorConfig config);
~Coordinator();
zx_status_t InitializeCoreDevices(const char* sys_device_driver);
bool InSuspend() 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;
// 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_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.
zx_status_t AddDevice(const fbl::RefPtr<Device>& parent, zx::channel rpc,
const uint64_t* props_data, size_t props_count, fbl::StringPiece name,
uint32_t protocol_id, fbl::StringPiece driver_path, fbl::StringPiece args,
bool invisible, zx::channel client_remote,
fbl::RefPtr<Device>* new_device);
zx_status_t RemoveDevice(const fbl::RefPtr<Device>& dev, bool forced);
zx_status_t MakeVisible(const fbl::RefPtr<Device>& dev);
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,
const zx_device_prop_t* props_data, size_t props_count,
const fuchsia_device_manager_DeviceComponent* components,
size_t components_count, uint32_t coresident_device_index);
void DmMexec(zx::vmo kernel, zx::vmo bootdata);
void HandleNewDevice(const fbl::RefPtr<Device>& dev);
zx_status_t PrepareProxy(const fbl::RefPtr<Device>& dev, Devhost* target_devhost);
void DumpState(VmoWriter* vmo) const;
const zx::resource& root_resource() const { return config_.root_resource; }
const zx::event& fshost_event() const { return config_.fshost_event; }
async_dispatcher_t* dispatcher() const { return config_.dispatcher; }
const devmgr::BootArgs& 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; }
bool suspend_debug() const { return config_.suspend_debug; }
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; }
bool system_loaded() const { return system_loaded_; }
void set_loader_service(DevhostLoaderService* loader_service) {
loader_service_ = loader_service;
}
fbl::DoublyLinkedList<Driver*, Driver::Node>& drivers() { return drivers_; }
const fbl::DoublyLinkedList<Driver*, Driver::Node>& drivers() const { return drivers_; }
fbl::DoublyLinkedList<fbl::RefPtr<Device>, Device::AllDevicesNode>& devices() {
return devices_;
}
const fbl::DoublyLinkedList<fbl::RefPtr<Device>, Device::AllDevicesNode>& devices() const {
return devices_;
}
void AppendPublishedMetadata(fbl::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);
SuspendContext& suspend_context() { return suspend_context_; }
const SuspendContext& suspend_context() const { return suspend_context_; }
const CompatibilityTestArgs& test_context() const { return test_context_; }
zx_status_t BindFidlServiceProxy(zx::channel listen_on);
zx_status_t BindOutgoingServices(zx::channel listen_on);
const Driver* component_driver() const { return component_driver_; }
void ReleaseDevhost(Devhost* dh);
// This method is public only for the test suite.
zx_status_t BindDriver(Driver* drv, const AttemptBindFunc& attempt_bind);
zx_status_t DriverCompatibiltyTest(const fbl::RefPtr<Device>& dev);
private:
CoordinatorConfig config_;
bool running_ = false;
bool launched_first_devhost_ = false;
bool system_available_ = false;
bool system_loaded_ = false;
DevhostLoaderService* loader_service_ = nullptr;
// Services offered to the rest of the system.
svc::Outgoing outgoing_services_;
// All Drivers
fbl::DoublyLinkedList<Driver*, Driver::Node> drivers_;
// Drivers to try last
fbl::DoublyLinkedList<Driver*, Driver::Node> fallback_drivers_;
// List of drivers loaded from /system by system_driver_loader()
fbl::DoublyLinkedList<Driver*, Driver::Node> system_drivers_;
// All Devices (excluding static immortal devices)
fbl::DoublyLinkedList<fbl::RefPtr<Device>, Device::AllDevicesNode> devices_;
// All DevHosts
fbl::DoublyLinkedList<Devhost*, Devhost::AllDevhostsNode> devhosts_;
// All composite devices
fbl::DoublyLinkedList<std::unique_ptr<CompositeDevice>, CompositeDevice::Node>
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_;
CompatibilityTestArgs test_context_;
fbl::DoublyLinkedList<fbl::unique_ptr<Metadata>, Metadata::Node> published_metadata_;
// Once the special component driver is loaded, this will refer to it. This
// driver is used for binding against components of composite devices
const Driver* component_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 Suspend(SuspendContext ctx, std::function<void(zx_status_t)> callback);
fbl::unique_ptr<Driver> ValidateDriver(fbl::unique_ptr<Driver> drv);
const Driver* LibnameToDriver(const fbl::String& libname) const;
zx_status_t NewDevhost(const char* name, Devhost* parent, Devhost** 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);
int RunCompatibilityTests();
void InitOutgoingServices();
};
bool driver_is_bindable(const Driver* drv, uint32_t protocol_id,
const fbl::Array<const zx_device_prop_t>& props, bool autobind);
// Path to driver that should be bound to components of composite devices
extern const char* kComponentDriverPath;
zx_status_t fidl_DmMexec(void* ctx, zx_handle_t raw_kernel, zx_handle_t raw_bootdata);
zx_status_t fidl_DirectoryWatch(void* ctx, uint32_t mask, uint32_t options,
zx_handle_t raw_watcher, fidl_txn_t* txn);
} // namespace devmgr