blob: 0b823e475aaddaff65cebafb7c1cd98d0d30ea7c [file] [log] [blame]
// Copyright 2019 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 "driver_host_loader_service.h"
#include <errno.h>
#include <fcntl.h>
#include <fuchsia/io/c/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/io.h>
#include <lib/fit/defer.h>
#include <stdint.h>
#include <zircon/status.h>
#include <array>
#include <memory>
#include <fbl/string_printf.h>
#include "coordinator.h"
#include "fdio.h"
#include "src/devices/lib/log/log.h"
#include "system_instance.h"
namespace {
static constexpr std::array<const char*, 3> kDriverWhitelist{
"libasync-default.so",
"libdriver.so",
"libfdio.so",
};
// Check if the driver is in the whitelist.
bool InWhitelist(const char* name) {
for (const char* driver : kDriverWhitelist) {
if (strcmp(driver, name) == 0) {
return true;
}
}
return false;
}
zx_status_t LoadObject(void* ctx, const char* name, zx_handle_t* vmo) {
if (!InWhitelist(name)) {
return ZX_ERR_ACCESS_DENIED;
}
auto self = static_cast<DriverHostLoaderService*>(ctx);
fbl::String path = fbl::StringPrintf("/boot/lib/%s", name);
int raw_fd;
zx_status_t status =
fdio_open_fd_at(self->root().get(), path.c_str(),
fuchsia_io_OPEN_RIGHT_READABLE | fuchsia_io_OPEN_RIGHT_EXECUTABLE, &raw_fd);
if (status != ZX_OK) {
return status;
}
fbl::unique_fd fd(raw_fd);
zx::vmo exec_vmo;
status = fdio_get_vmo_exec(fd.get(), exec_vmo.reset_and_get_address());
if (status != ZX_OK) {
return status;
}
status = exec_vmo.set_property(ZX_PROP_NAME, path.c_str(), path.size());
if (status != ZX_OK) {
return status;
}
*vmo = exec_vmo.release();
return ZX_OK;
}
zx_status_t LoadAbspath(void* ctx, const char* path, zx_handle_t* vmo) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t PublishDataSink(void* ctx, const char* name, zx_handle_t vmo) {
zx_handle_close(vmo);
return ZX_ERR_NOT_SUPPORTED;
}
constexpr loader_service_ops_t ops_{
.load_object = LoadObject,
.load_abspath = LoadAbspath,
.publish_data_sink = PublishDataSink,
.finalizer = nullptr,
};
} // namespace
zx_status_t DriverHostLoaderService::Create(async_dispatcher_t* dispatcher,
SystemInstance* system_instance,
std::unique_ptr<DriverHostLoaderService>* out) {
fdio_ns_t* ns;
zx_status_t status = fdio_ns_create(&ns);
if (status != ZX_OK) {
LOGF(ERROR, "Failed to create namespace: %s", zx_status_get_string(status));
return status;
}
auto defer = fit::defer([ns] { fdio_ns_destroy(ns); });
zx::channel boot_client, boot_server;
status = zx::channel::create(0, &boot_client, &boot_server);
if (status != ZX_OK) {
return status;
}
status = fdio_open("/boot", FS_READONLY_DIR_FLAGS, boot_server.release());
if (status != ZX_OK) {
LOGF(ERROR, "Failed to connect to '/boot': %s", zx_status_get_string(status));
return status;
}
status = fdio_ns_bind(ns, "/boot", boot_client.release());
if (status != ZX_OK) {
LOGF(ERROR, "Failed to bind namespace '/boot': %s", zx_status_get_string(status));
return status;
}
fbl::unique_fd root(fdio_ns_opendir(ns));
if (!root) {
LOGF(ERROR, "Failed to open root directory");
return ZX_ERR_IO;
}
std::unique_ptr<DriverHostLoaderService> ldsvc(new DriverHostLoaderService);
status = loader_service_create(dispatcher, &ops_, ldsvc.get(), &ldsvc->svc_);
if (status != ZX_OK) {
LOGF(ERROR, "Failed to create loader service: %s", zx_status_get_string(status));
return status;
}
ldsvc->root_ = std::move(root);
*out = std::move(ldsvc);
return ZX_OK;
}
DriverHostLoaderService::~DriverHostLoaderService() {
if (svc_ != nullptr) {
loader_service_release(svc_);
}
}
zx_status_t DriverHostLoaderService::Connect(zx::channel* out) {
return loader_service_connect(svc_, out->reset_and_get_address());
}