blob: 2ecd92fdf1b2893eee42bd5f892a25965e986474 [file] [log] [blame]
// Copyright 2020 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/package_resolver.h"
#include <fidl/fuchsia.io/cpp/wire.h>
#include <fidl/fuchsia.pkg/cpp/wire.h>
#include <lib/fdio/directory.h>
#include <lib/service/llcpp/service.h>
#include <fbl/string_printf.h>
#include "src/devices/lib/log/log.h"
#include "src/lib/storage/vfs/cpp/vfs.h"
namespace fio = fuchsia_io;
namespace internal {
zx::status<std::unique_ptr<Driver>> PackageResolver::FetchDriver(const std::string& package_url) {
component::FuchsiaPkgUrl parsed_url;
if (!parsed_url.Parse(std::string(package_url))) {
LOGF(ERROR, "Failed to parse package url: %s", package_url.data());
return zx::error(ZX_ERR_INTERNAL);
}
auto package_dir_result = Resolve(parsed_url);
if (!package_dir_result.is_ok()) {
LOGF(ERROR, "Failed to resolve package url %s, err %d", parsed_url.ToString().c_str(),
package_dir_result.status_value());
return package_dir_result.take_error();
}
auto driver_vmo_result = LoadDriver(package_dir_result.value(), parsed_url);
if (driver_vmo_result.status_value()) {
return driver_vmo_result.take_error();
}
Driver* driver = nullptr;
DriverLoadCallback callback = [&driver](Driver* d, const char* version) mutable { driver = d; };
zx_status_t status = load_driver_vmo(boot_args_, std::string_view(parsed_url.ToString()),
std::move(driver_vmo_result.value()), std::move(callback));
if (status != ZX_OK) {
return zx::error(status);
}
fbl::unique_fd package_dir_fd;
status = fdio_fd_create(package_dir_result.value().TakeClientEnd().TakeChannel().release(),
package_dir_fd.reset_and_get_address());
if (status != ZX_OK) {
LOGF(ERROR, "Failed to create package_dir_fd: %sd", zx_status_get_string(status));
return zx::error(status);
}
driver->package_dir = std::move(package_dir_fd);
return zx::ok(std::unique_ptr<Driver>(driver));
}
zx_status_t PackageResolver::ConnectToResolverService() {
auto client_end = service::Connect<fuchsia_pkg::PackageResolver>();
if (client_end.is_error()) {
return client_end.error_value();
}
resolver_client_ = fidl::BindSyncClient(std::move(*client_end));
return ZX_OK;
}
zx::status<fidl::WireSyncClient<fio::Directory>> PackageResolver::Resolve(
const component::FuchsiaPkgUrl& package_url) {
if (!resolver_client_.is_valid()) {
zx_status_t status = ConnectToResolverService();
if (status != ZX_OK) {
LOGF(ERROR, "Failed to connect to package resolver service");
return zx::error(status);
}
}
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (endpoints.is_error()) {
return endpoints.take_error();
}
// This is synchronous for now so we can get the proof of concept working.
// Eventually we will want to do this asynchronously.
auto result = resolver_client_->Resolve(
::fidl::StringView(fidl::StringView::FromExternal(package_url.package_path())),
std::move(endpoints->server));
if (!result.ok() || result->is_error()) {
LOGF(ERROR, "Failed to resolve package");
if (!result.ok()) {
return zx::error(ZX_ERR_INTERNAL);
} else {
switch (result->error_value()) {
case fuchsia_pkg::wire::ResolveError::kIo:
return zx::error(ZX_ERR_IO);
case fuchsia_pkg::wire::ResolveError::kAccessDenied:
return zx::error(ZX_ERR_ACCESS_DENIED);
case fuchsia_pkg::wire::ResolveError::kRepoNotFound:
return zx::error(ZX_ERR_NOT_FOUND);
case fuchsia_pkg::wire::ResolveError::kPackageNotFound:
return zx::error(ZX_ERR_NOT_FOUND);
case fuchsia_pkg::wire::ResolveError::kUnavailableBlob:
return zx::error(ZX_ERR_UNAVAILABLE);
case fuchsia_pkg::wire::ResolveError::kInvalidUrl:
return zx::error(ZX_ERR_INVALID_ARGS);
case fuchsia_pkg::wire::ResolveError::kNoSpace:
return zx::error(ZX_ERR_NO_SPACE);
default:
return zx::error(ZX_ERR_INTERNAL);
}
}
}
return zx::ok(fidl::BindSyncClient(std::move(endpoints->client)));
}
zx::status<zx::vmo> PackageResolver::LoadDriver(
const fidl::WireSyncClient<fuchsia_io::Directory>& package_dir,
const component::FuchsiaPkgUrl& package_url) {
const fio::wire::OpenFlags kFileRights =
fio::wire::OpenFlags::kRightReadable | fio::wire::OpenFlags::kRightExecutable;
const fio::wire::VmoFlags kDriverVmoFlags =
fio::wire::VmoFlags::kRead | fio::wire::VmoFlags::kExecute;
// Open and duplicate the driver vmo.
auto endpoints = fidl::CreateEndpoints<fuchsia_io::File>();
if (endpoints.is_error()) {
return endpoints.take_error();
}
auto file_open_result = package_dir->Open(
kFileRights, 0u /* mode */,
::fidl::StringView(fidl::StringView::FromExternal(package_url.resource_path())),
fidl::ServerEnd<fuchsia_io::Node>(endpoints->server.TakeChannel()));
if (!file_open_result.ok()) {
LOGF(ERROR, "Failed to open driver file: %s", package_url.resource_path().c_str());
return zx::error(ZX_ERR_INTERNAL);
}
auto file_client = fidl::BindSyncClient(std::move(endpoints->client));
fidl::WireResult file_res = file_client->GetBackingMemory(kDriverVmoFlags);
if (!file_res.ok()) {
LOGF(ERROR, "Failed to get driver vmo: %s", file_res.FormatDescription().c_str());
return zx::error(ZX_ERR_INTERNAL);
}
const auto* res = file_res.Unwrap();
if (res->is_error()) {
LOGF(ERROR, "Failed to get driver vmo: %s", zx_status_get_string(res->error_value()));
return zx::error(ZX_ERR_INTERNAL);
}
return zx::ok(std::move(res->value()->vmo));
}
} // namespace internal