blob: f36eb233f6eb9c53605df53b01c0b291cd2c2307 [file] [log] [blame]
// 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