| // Copyright 2022 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/driver_host.h" |
| |
| #include <lib/driver/component/cpp/internal/start_args.h> |
| |
| #include <memory> |
| |
| #include "lib/vfs/cpp/pseudo_file.h" |
| #include "src/devices/lib/log/log.h" |
| #include "src/lib/fsl/handles/object_info.h" |
| |
| namespace fdh = fuchsia_driver_host; |
| namespace frunner = fuchsia_component_runner; |
| namespace fdf { |
| using namespace fuchsia_driver_framework; |
| } // namespace fdf |
| namespace driver_manager { |
| |
| namespace { |
| std::unique_ptr<vfs::PseudoFile> CreateReadonlyFile( |
| fit::function<zx::result<std::string>()> content_producer) { |
| auto read_fn = [content_producer = std::move(content_producer)](std::vector<uint8_t>* output, |
| size_t max_file_size) { |
| zx::result<std::string> contents = content_producer(); |
| if (contents.is_error()) { |
| return contents.status_value(); |
| } |
| output->resize(contents->length()); |
| std::copy(contents->begin(), contents->end(), output->begin()); |
| return ZX_OK; |
| }; |
| |
| return std::make_unique<vfs::PseudoFile>(30, std::move(read_fn)); |
| } |
| } // namespace |
| |
| zx::result<> SetEncodedConfig(fidl::WireTableBuilder<fdf::wire::DriverStartArgs>& args, |
| frunner::wire::ComponentStartInfo& start_info) { |
| if (!start_info.has_encoded_config()) { |
| return zx::ok(); |
| } |
| |
| if (!start_info.encoded_config().is_buffer() && !start_info.encoded_config().is_bytes()) { |
| LOGF(ERROR, "Failed to parse encoded config in start info. Encoding is not buffer or bytes."); |
| return zx::error(ZX_ERR_INVALID_ARGS); |
| } |
| |
| if (start_info.encoded_config().is_buffer()) { |
| args.config(std::move(start_info.encoded_config().buffer().vmo)); |
| return zx::ok(); |
| } |
| |
| auto vmo_size = start_info.encoded_config().bytes().count(); |
| zx::vmo vmo; |
| |
| auto status = zx::vmo::create(vmo_size, ZX_RIGHT_TRANSFER | ZX_RIGHT_READ, &vmo); |
| if (status != ZX_OK) { |
| return zx::error(status); |
| } |
| |
| status = vmo.write(start_info.encoded_config().bytes().data(), 0, vmo_size); |
| if (status != ZX_OK) { |
| return zx::error(status); |
| } |
| |
| args.config(std::move(vmo)); |
| return zx::ok(); |
| } |
| |
| DriverHostComponent::DriverHostComponent( |
| fidl::ClientEnd<fdh::DriverHost> driver_host, async_dispatcher_t* dispatcher, |
| fbl::DoublyLinkedList<std::unique_ptr<DriverHostComponent>>* driver_hosts, |
| std::shared_ptr<bool> server_connected) |
| : driver_host_(std::move(driver_host), dispatcher, |
| fidl::ObserveTeardown([this, driver_hosts] { driver_hosts->erase(*this); })), |
| dispatcher_(dispatcher), |
| server_connected_(std::move(server_connected)) { |
| InitializeElfDir(); |
| } |
| |
| void DriverHostComponent::InitializeElfDir() { |
| // This directory allows zxdb to conn |
| auto elf_dir = std::make_unique<vfs::PseudoDir>(); |
| |
| auto now = std::to_string(zx::clock::get_monotonic().get()); |
| elf_dir->AddEntry("process_start_time", CreateReadonlyFile([now]() { return zx::ok(now); })); |
| |
| elf_dir->AddEntry("job_id", CreateReadonlyFile([this]() -> zx::result<std::string> { |
| zx::result job_id = GetJobKoid(); |
| if (job_id.is_error()) { |
| return job_id.take_error(); |
| } |
| return zx::ok(std::to_string(*job_id)); |
| })); |
| |
| elf_dir->AddEntry("process_id", CreateReadonlyFile([this]() -> zx::result<std::string> { |
| zx::result process_id = GetProcessKoid(); |
| if (process_id.is_error()) { |
| return process_id.take_error(); |
| } |
| return zx::ok(std::to_string(*process_id)); |
| })); |
| |
| runtime_dir_.AddEntry("elf", std::move(elf_dir)); |
| } |
| |
| void DriverHostComponent::Start( |
| fidl::ClientEnd<fdf::Node> client_end, std::string node_name, |
| fuchsia_driver_framework::wire::NodePropertyDictionary node_properties, |
| fidl::VectorView<fuchsia_driver_framework::wire::NodeSymbol> symbols, |
| frunner::wire::ComponentStartInfo start_info, |
| fidl::ServerEnd<fuchsia_driver_host::Driver> driver, StartCallback cb) { |
| auto binary = fdf_internal::ProgramValue(start_info.program(), "binary").value_or(""); |
| fidl::Arena arena; |
| auto args = fdf::wire::DriverStartArgs::Builder(arena); |
| args.node(std::move(client_end)) |
| .node_name(fidl::StringView::FromExternal(node_name)) |
| .node_properties(node_properties) |
| .url(start_info.resolved_url()) |
| .program(start_info.program()) |
| .incoming(start_info.ns()) |
| .outgoing_dir(std::move(start_info.outgoing_dir())); |
| |
| auto status = SetEncodedConfig(args, start_info); |
| if (status.is_error()) { |
| cb(status.take_error()); |
| return; |
| } |
| |
| if (!symbols.empty()) { |
| args.symbols(symbols); |
| } |
| |
| if (start_info.has_runtime_dir()) { |
| runtime_dir_.Serve(fuchsia::io::OpenFlags::RIGHT_READABLE, |
| start_info.runtime_dir().TakeChannel(), dispatcher_); |
| } |
| |
| driver_host_->Start(args.Build(), std::move(driver)) |
| .ThenExactlyOnce([cb = std::move(cb), binary = std::move(binary)](auto& result) mutable { |
| if (!result.ok()) { |
| LOGF(ERROR, "Failed to start driver '%s' in driver host: %s", binary.c_str(), |
| result.FormatDescription().c_str()); |
| cb(zx::error(result.status())); |
| return; |
| } |
| if (result->is_error()) { |
| LOGF(ERROR, "Failed to start driver '%s' in driver host: %s", binary.c_str(), |
| zx_status_get_string(result->error_value())); |
| cb(result->take_error()); |
| return; |
| } |
| cb(zx::ok()); |
| }); |
| } |
| |
| zx::result<fuchsia_driver_host::ProcessInfo> DriverHostComponent::GetProcessInfo() const { |
| if (process_info_) { |
| return zx::ok(*process_info_); |
| } |
| |
| if (!(*server_connected_)) { |
| return zx::error(ZX_ERR_SHOULD_WAIT); |
| } |
| |
| fidl::WireResult result = driver_host_.sync()->GetProcessInfo(); |
| if (!result.ok()) { |
| return zx::error(result.status()); |
| } |
| if (result->is_error()) { |
| return zx::error(result->error_value()); |
| } |
| process_info_ = fidl::ToNatural(*result->value()); |
| return zx::ok(*process_info_); |
| } |
| |
| zx::result<uint64_t> DriverHostComponent::GetJobKoid() const { |
| zx::result result = GetProcessInfo(); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| return zx::ok(result->job_koid()); |
| } |
| |
| zx::result<uint64_t> DriverHostComponent::GetProcessKoid() const { |
| zx::result result = GetProcessInfo(); |
| if (result.is_error()) { |
| return result.take_error(); |
| } |
| return zx::ok(result->process_koid()); |
| } |
| |
| zx::result<> DriverHostComponent::InstallLoader( |
| fidl::ClientEnd<fuchsia_ldsvc::Loader> loader_client) const { |
| auto result = driver_host_->InstallLoader(std::move(loader_client)); |
| if (!result.ok()) { |
| return zx::error(result.status()); |
| } |
| return zx::ok(); |
| } |
| |
| } // namespace driver_manager |