blob: 6fd77e53090c40f1b63d1894c96377579b936016 [file] [log] [blame]
// Copyright 2024 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_LOADER_LOADER_H_
#define SRC_DEVICES_BIN_DRIVER_LOADER_LOADER_H_
#include <fidl/fuchsia.driver.loader/cpp/wire.h>
#include <fidl/fuchsia.io/cpp/fidl.h>
#include <lib/async/dispatcher.h>
#include <lib/fdio/directory.h>
#include <lib/ld/remote-dynamic-linker.h>
#include <lib/ld/remote-load-module.h>
#include <lib/zircon-internal/default_stack_size.h>
#include <lib/zx/process.h>
#include <lib/zx/thread.h>
#include <lib/zx/vmar.h>
#include <lib/zx/vmo.h>
#include <memory>
#include <optional>
#include <fbl/intrusive_double_list.h>
#include "src/devices/bin/driver_loader/diagnostics.h"
namespace driver_loader {
class Loader : public fidl::WireServer<fuchsia_driver_loader::DriverHostLauncher> {
public:
using Linker = ld::RemoteDynamicLinker<>;
// This is the ABI returned from dynamic linking that provides immutable access to
// the data structures and symbols of loaded modules.
using DynamicLinkingPassiveAbi = Linker::size_type;
using LoadDriverHandler = fit::function<void(zx::unowned_channel bootstrap_sender,
DynamicLinkingPassiveAbi passive_abi)>;
// If |load_driver_handler_for_testing| is provided, it will be called each time a driver has been
// loaded. This allows tests to inject custom bootstrap behavior.
static std::unique_ptr<Loader> Create(async_dispatcher_t* dispatcher,
LoadDriverHandler load_driver_handler_for_testing = {});
void Connect(fidl::ServerEnd<fuchsia_driver_loader::DriverHostLauncher> server_end);
// fidl::WireServer<fuchsia_driver_loader::DriverHostLauncher>
void Launch(LaunchRequestView request, LaunchCompleter::Sync& completer) override;
void handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_driver_loader::DriverHostLauncher> md,
fidl::UnknownMethodCompleter::Sync& completer) override {
completer.Close(ZX_ERR_UNAVAILABLE);
}
private:
using RemoteModule = ld::RemoteLoadModule<>;
inline static const size_t kPageSize = zx_system_get_page_size();
inline static constexpr size_t kDefaultStackSize = ZIRCON_DEFAULT_STACK_SIZE;
// Stores the state for a started process.
class ProcessState : public fidl::WireServer<fuchsia_driver_loader::DriverHost>,
public fbl::DoublyLinkedListable<std::unique_ptr<ProcessState>> {
public:
struct ProcessStartArgs {
// Entry point for the thread.
uintptr_t entry = 0;
// Base pointer of the loaded vdso.
uintptr_t vdso_base = 0;
// Stack size request from the executable's PT_GNU_STACK, if any.
std::optional<size_t> stack_size = std::nullopt;
};
static zx::result<std::unique_ptr<ProcessState>> Create(
ld::RemoteAbiStub<>::Ptr remote_abi_stub, zx::process process, zx::thread thread,
zx::vmar root_vmar, zx::channel bootstrap_sender,
LoadDriverHandler load_driver_handler_for_testing,
const ProcessStartArgs& process_start_args, Linker::InitModuleList preloaded_modules);
// fidl::WireServer<fuchsia_driver_loader::DriverHost>
void LoadDriver(LoadDriverRequestView request, LoadDriverCompleter::Sync& completer) override;
void handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_driver_loader::DriverHost> md,
fidl::UnknownMethodCompleter::Sync& completer) override {
completer.Close(ZX_ERR_UNAVAILABLE);
}
zx_status_t BindServer(async_dispatcher_t* dispatcher,
fidl::ServerEnd<fuchsia_driver_loader::DriverHost> server_end);
// Begins execution of the process.
zx_status_t Start(zx::channel bootstrap_receiver);
private:
// TODO(https://fxbug.dev/357948288): this is a placeholder until we can sub in
// the actual start function signature.
static constexpr std::string_view kDriverStartSymbol = "DriverStart";
// Use |Create| instead.
ProcessState() = default;
// Loads a driver module into the driver host process.
// Returns the dynamic linking passive ABI for the loaded modules.
zx::result<DynamicLinkingPassiveAbi> LoadDriverModule(
std::string driver_name, zx::vmo driver_module,
fidl::ClientEnd<fuchsia_io::Directory> lib_dir,
fidl::VectorView<fuchsia_driver_loader::wire::RootModule> additional_root_modules);
// Allocates the stack for the initial thread of the process.
zx_status_t AllocateStack();
ld::RemoteAbiStub<>::Ptr remote_abi_stub_;
zx::process process_;
zx::thread thread_;
zx::vmar root_vmar_;
// Connection to the started driver host process for sending bootstrap messages.
zx::channel bootstrap_sender_;
// Handler to call after a driver is loaded.
LoadDriverHandler load_driver_handler_for_testing_;
ProcessStartArgs process_start_args_;
// Stack for the initial thread allocated by |AllocateStack|.
zx::vmo stack_;
uintptr_t initial_stack_pointer_ = 0;
std::optional<fidl::ServerBinding<fuchsia_driver_loader::DriverHost>> binding_;
Linker::InitModuleList preloaded_modules_;
};
// Use |Create| instead.
explicit Loader(async_dispatcher_t* dispatcher, LoadDriverHandler load_driver_handler_for_testing,
Diagnostics& diag, zx::vmo stub_ld_vmo)
: dispatcher_(dispatcher),
load_driver_handler_for_testing_(std::move(load_driver_handler_for_testing)),
remote_abi_stub_(ld::RemoteAbiStub<>::Create(diag, std::move(stub_ld_vmo), kPageSize)) {}
// Launches the |exec| driver host binary into |process|.
zx::result<> Start(zx::process process, zx::vmar root_vmar, zx::vmo exec, zx::vmo vdso,
fidl::ClientEnd<fuchsia_io::Directory> lib_dir,
fidl::ServerEnd<fuchsia_driver_loader::DriverHost> driver_host);
// Retrieves the vmo for |libname| from |lib_dir|.
static zx::result<zx::vmo> GetDepVmo(fidl::UnownedClientEnd<fuchsia_io::Directory> lib_dir,
const char* libname);
// Returns a closure that can be passed to |ld::RemoteDynamicLinker::Init|.
static auto GetDepFunction(Diagnostics& diag, fidl::ClientEnd<fuchsia_io::Directory> lib_dir) {
return [&diag, lib_dir = std::move(lib_dir)](
const RemoteModule::Soname& soname) -> Linker::GetDepResult {
auto result = GetDepVmo(lib_dir, soname.c_str());
if (result.is_ok()) {
return RemoteModule::Decoded::Create(diag, std::move(*result), kPageSize);
}
// |MissingDependency| or |SystemError| will return true if we should continue
// processing even on error, or false if we should abort early.
if (result.error_value() == ZX_ERR_NOT_FOUND
? !diag.MissingDependency(soname.str())
: !diag.SystemError("cannot open dependency ", soname.str(), ": ",
elfldltl::ZirconError{result.error_value()})) {
// Tell the linker to abort.
return std::nullopt;
}
// Return an empty pointer. This tells the linker to try to continue even with the error.
return RemoteModule::Decoded::Ptr();
};
}
async_dispatcher_t* dispatcher_ = nullptr;
LoadDriverHandler load_driver_handler_for_testing_;
ld::RemoteAbiStub<>::Ptr remote_abi_stub_;
fbl::DoublyLinkedList<std::unique_ptr<ProcessState>> started_processes_;
fidl::ServerBindingGroup<fuchsia_driver_loader::DriverHostLauncher> bindings_;
};
} // namespace driver_loader
#endif // SRC_DEVICES_BIN_DRIVER_LOADER_LOADER_H_